-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from Abidoyesimze/canvasOnchain
Change the branch name to canvasOnchain and also write a readme file in the hardhat folder for description
- Loading branch information
Showing
6 changed files
with
1,005 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
## PixelCanvas: Decentralized Collaborative Pixel Art Smart Contract | ||
|
||
### Contract Overview | ||
PixelCanvas is an Ethereum smart contract that enables collaborative pixel art creation on a fixed 64x64 canvas with a predefined color palette. | ||
|
||
### Technical Specifications | ||
- **Blockchain**: Ethereum | ||
- **Solidity Version**: ^0.8.20 | ||
- **Dependencies**: OpenZeppelin Ownable | ||
- **Canvas Dimensions**: 64x64 pixels | ||
- **Color Palette**: 8 predefined colors | ||
|
||
### Key Components | ||
|
||
#### Pixel Structure | ||
```solidity | ||
struct Pixel { | ||
address author; // Address of pixel creator | ||
Color color; // Chosen color from enum | ||
uint256 timestamp;// Block timestamp of pixel placement | ||
} | ||
Core Functionalities | ||
1. Pixel Placement | ||
Function: placePixel(uint256 x, uint256 y, Color color) | ||
Validates coordinate boundaries | ||
Allows user to place a single pixel | ||
Emits PixelPlaced event for tracking | ||
2. Canvas Retrieval | ||
getPixel(x, y): Retrieves individual pixel information | ||
initializeBuidlGuidlLogo(): Sets initial canvas state with Batch11 logo | ||
3. Contract Management | ||
withdraw(): Allows owner to withdraw contract balance | ||
Accepts Ether via fallback() and receive() functions | ||
4. Initialization | ||
Test the PixelCanvas contract | ||
Includes default Buidlguidl Batch11 logo on contract deployment | ||
Demonstrates initial canvas state | ||
Current Limitations | ||
Fixed canvas size (64x64) | ||
No pixel modification after placement | ||
Limited to 8 colors | ||
Only contract owner can withdraw funds | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
contract PixelCanvas is Ownable { | ||
// Canvas Dimensions | ||
uint256 public constant CANVAS_WIDTH = 64; | ||
uint256 public constant CANVAS_HEIGHT = 64; | ||
|
||
// Color Palette (Limited Options) | ||
enum Color { | ||
WHITE, | ||
BLACK, | ||
RED, | ||
GREEN, | ||
BLUE, | ||
YELLOW, | ||
PURPLE, | ||
ORANGE | ||
} | ||
|
||
// Pixel Structure | ||
struct Pixel { | ||
address author; | ||
Color color; | ||
uint256 timestamp; | ||
} | ||
|
||
// Mapping to store modified pixels | ||
mapping(uint256 => mapping(uint256 => Pixel)) public canvas; | ||
|
||
|
||
// Events | ||
event PixelPlaced(address indexed author, uint256 x, uint256 y, Color color); | ||
event Withdrawal(address indexed owner, uint256 amount); | ||
|
||
// Constructor to initialize default Buidlguidl Batch11 drawing | ||
constructor() Ownable(msg.sender){ | ||
|
||
// Initial drawing representing Buidlguidl Batch11 logo | ||
initializeBuidlGuidlLogo(); | ||
} | ||
|
||
/** | ||
* @dev Initialize a default Buidlguidl Batch11 inspired pixel art | ||
* This is a simplified representation and can be customized | ||
*/ | ||
function initializeBuidlGuidlLogo() private { | ||
for (uint256 x = 10; x < 20; x++) { | ||
for (uint256 y = 10; y < 50; y++) { | ||
canvas[x][y] = Pixel({ | ||
author: msg.sender, | ||
color: Color.BLUE, | ||
timestamp: block.timestamp | ||
}); | ||
} | ||
} | ||
|
||
// 11 representation with some pixels | ||
for (uint256 x = 30; x < 40; x++) { | ||
for (uint256 y = 20; y < 30; y++) { | ||
canvas[x][y] = Pixel({ | ||
author: msg.sender, | ||
color: Color.GREEN, | ||
timestamp: block.timestamp | ||
}); | ||
} | ||
} | ||
|
||
// Add some distinctive pixels to represent Buidlguidl spirit | ||
canvas[32][25] = Pixel({ | ||
author: msg.sender, | ||
color: Color.RED, | ||
timestamp: block.timestamp | ||
}); | ||
} | ||
|
||
/** | ||
* @dev Place a pixel on the canvas | ||
* @param x X-coordinate of the pixel | ||
* @param y Y-coordinate of the pixel | ||
* @param color Color of the pixel | ||
*/ | ||
function placePixel(uint256 x, uint256 y, Color color) external { | ||
require(x < CANVAS_WIDTH, "X coordinate out of bounds"); | ||
require(y < CANVAS_HEIGHT, "Y coordinate out of bounds"); | ||
|
||
canvas[x][y] = Pixel({ | ||
author: msg.sender, | ||
color: color, | ||
timestamp: block.timestamp | ||
}); | ||
|
||
emit PixelPlaced(msg.sender, x, y, color); | ||
} | ||
|
||
|
||
/** | ||
* @dev Get pixel information | ||
* @param x X-coordinate of the pixel | ||
* @param y Y-coordinate of the pixel | ||
* @return Pixel details | ||
*/ | ||
function getPixel(uint256 x, uint256 y) external view returns (Pixel memory) { | ||
require(x < CANVAS_WIDTH, "X coordinate out of bounds"); | ||
require(y < CANVAS_HEIGHT, "Y coordinate out of bounds"); | ||
return canvas[x][y]; | ||
} | ||
|
||
|
||
|
||
function withdraw() external onlyOwner { | ||
uint256 balance = address(this).balance; | ||
require(balance > 0, "No funds to withdraw"); | ||
|
||
|
||
(bool success, ) = payable(owner()).call{value: balance}(""); | ||
require(success, "Transfer failed"); | ||
|
||
|
||
emit Withdrawal(owner(), balance); | ||
} | ||
|
||
// Fallback and receive functions to accept Ether | ||
fallback() external payable {} | ||
|
||
receive() external payable {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; | ||
import { expect } from "chai"; | ||
import hre from "hardhat"; | ||
|
||
describe("PixelCanvas", function () { | ||
async function deployPixelCanvas() { | ||
const [owner, user1, user2] = await hre.ethers.getSigners(); | ||
const pixel = await hre.ethers.getContractFactory("PixelCanvas"); | ||
const Pixel = await pixel.deploy(); | ||
return { owner, user1, user2, Pixel }; | ||
} | ||
|
||
describe("Deployment", function () { | ||
it("Should set the correct owner", async function () { | ||
const { owner, Pixel } = await loadFixture(deployPixelCanvas); | ||
expect(await Pixel.owner()).to.equal(owner.address); | ||
}); | ||
|
||
it("Should initialize canvas with predefined pixels", async function () { | ||
// Check a few predefined pixels from initializeBuidlGuidlLogo() | ||
const { owner, Pixel } = await loadFixture(deployPixelCanvas); | ||
const bluePixel = await Pixel.canvas(15, 25); | ||
expect(bluePixel.color).to.equal(4); // Blue is enum index 1 | ||
expect(bluePixel.author).to.equal(owner.address); | ||
}); | ||
}); | ||
|
||
describe("Pixel Placement", function () { | ||
it("Should allow placing a pixel", async function () { | ||
const x = 10; | ||
const y = 20; | ||
const color = 2; // Red from enum | ||
const { user1, Pixel } = await loadFixture(deployPixelCanvas); | ||
// Place pixel from user1 | ||
await Pixel.connect(user1).placePixel(x, y, color); | ||
|
||
const pixel = await Pixel.canvas(x, y); | ||
expect(pixel.author).to.equal(user1.address); | ||
expect(pixel.color).to.equal(color); | ||
}); | ||
|
||
it("Should reject out-of-bounds pixel placement", async function () { | ||
const { Pixel } = await loadFixture(deployPixelCanvas); | ||
await expect(Pixel.placePixel(64, 10, 0)).to.be.revertedWith("X coordinate out of bounds"); | ||
|
||
await expect(Pixel.placePixel(10, 64, 0)).to.be.revertedWith("Y coordinate out of bounds"); | ||
}); | ||
|
||
it("Should emit PixelPlaced event", async function () { | ||
const x = 30; | ||
const y = 40; | ||
|
||
const color = 3; // Green from enum | ||
const { user1, Pixel } = await loadFixture(deployPixelCanvas); | ||
await expect(Pixel.connect(user1).placePixel(x, y, color)) | ||
.to.emit(Pixel, "PixelPlaced") | ||
.withArgs(user1.address, x, y, color); | ||
}); | ||
}); | ||
|
||
describe("Pixel Retrieval", function () { | ||
it("Should retrieve pixel information", async function () { | ||
const x = 12; | ||
const y = 15; | ||
const color = 4; // Blue from enum | ||
const { user1, Pixel } = await loadFixture(deployPixelCanvas); | ||
await Pixel.connect(user1).placePixel(x, y, color); | ||
|
||
const pixel = await Pixel.getPixel(x, y); | ||
expect(pixel.author).to.equal(user1.address); | ||
expect(pixel.color).to.equal(color); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.