Skip to content

Commit

Permalink
V1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
0xvandal committed Jan 16, 2022
0 parents commit 409fc7d
Show file tree
Hide file tree
Showing 18 changed files with 10,186 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MNEMONIC_LOCAL=
MNEMONIC_PROD=
KOVAN_DEPLOYER_PRIVATE_KEY=
MAINNET_DEPLOYER_PRIVATE_KEY=
REPORT_GAS=true
INFURA_API_KEY=
ALCHEMY_KEY=
ALCHEMY_KOVAN_KEY=
ETHERSCAN_API_KEY=
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules

#Hardhat files
cache
artifacts
factories
typechain

.env
.vscode
17 changes: 17 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"endOfLine":"auto",
"printWidth": 120,
"singleQuote": false,
"tabWidth": 2,
"trailingComma": "all",
"overrides": [
{
"files": "*.sol",
"options": {
"tabWidth": 4
}
}
]
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Alex Manuskin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Contracts Deployment

Set `.env` variables.

`npx hardhat node`

`npx hardhat run scripts/dappCalls.ts --network localhost` (To test against local network with some minted)

https://etherscan.io/address/0xbfE6B3B37e1Bd624b6eE25FdAEa13B12446ed799#code

# Solidity Template

Uses

- [Hardhat](https://github.com/nomiclabs/hardhat): compile and run the smart contracts on a local development network
- [TypeChain](https://github.com/ethereum-ts/TypeChain): generate TypeScript types for smart contracts
- [Ethers](https://github.com/ethers-io/ethers.js/): renowned Ethereum library and wallet implementation
- [Waffle](https://github.com/EthWorks/Waffle): tooling for writing comprehensive smart contract tests
- [Solhint](https://github.com/protofire/solhint): linter
- [Prettier Plugin Solidity](https://github.com/prettier-solidity/prettier-plugin-solidity): code formatter

This is a GitHub template, which means you can reuse it as many times as you want. You can do that by clicking the "Use this
template" button at the top of the page.

## Usage

### Pre Requisites

Before running any command, make sure to install dependencies:

```sh
$ yarn install
```

### Compile

Compile the smart contracts with Hardhat:

```sh
$ yarn compile
```

### Test

Run the Mocha tests:

```sh
$ yarn test
```

### Deploy contract to netowrk (requires Mnemonic and infura API key)

```
npx hardhat run --network rinkeby ./scripts/deploy.ts
```

### Validate a contract with etherscan (requires API ke)

```
npx hardhat verify --network <network> <DEPLOYED_CONTRACT_ADDRESS> "Constructor argument 1"
```

### Added plugins

- Gas reporter [hardhat-gas-reporter](https://hardhat.org/plugins/hardhat-gas-reporter.html)
- Etherscan [hardhat-etherscan](https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html)

## Thanks

If you like it than you soulda put a start ⭐ on it
179 changes: 179 additions & 0 deletions contracts/NFT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "base64-sol/base64.sol";
import "./Render.sol";

interface Protocol {
function tokenURI(uint256) external view returns (string memory);
}

contract RightClickNFT is ERC721URIStorage {
address payable private vandal_king;
address private constant blackhole = 0x0000000000000000000000000000000000000000;
uint256 private _vandalId;

event Vandalized(address user, address target, uint256 targetId, uint256 vToken);
event CleanedUp(address user, uint256 target, uint256 vToken);

struct vTag {
address nft;
string tokenId;
string tag;
uint256 status; // 0 - original mint() 1 - burned mint() 2 - switcheeero for original mint() after burn()
uint256 trait;
}

mapping(bytes => bool) public isTagged;
mapping(uint256 => vTag) public vandal;

uint256 public initMintPrice;
Render public svgstorage;

constructor(address payable creator) ERC721("VandalNeu", "CR") {
vandal_king = creator;
initMintPrice = 0.01377 ether;
svgstorage = new Render();
}

function mint(
address nft,
uint256 tokenId,
string memory tag
) public payable returns (uint256) {
bytes memory hashed = abi.encode(nft, tokenId);
require(!isTagged[hashed], "already defaced");
require(nft != address(this), "loop deface forbidden");
require(bytes(tag).length < 30, "tag max len = 30");
initMintPrice = getCurrentPriceToMint();
require(msg.value >= initMintPrice, "not enough eth sent");

isTagged[hashed] = true;
uint256 vandalId = _vandalId++;

Protocol vandal_target = Protocol(nft);
string memory original_uri = vandal_target.tokenURI(tokenId); // copy original uri
string memory _tokenId = svgstorage.toString(tokenId);

// trim tokenId length, ie. some tokens can be very long, destroys svg
if (bytes(_tokenId).length > 30) {
_tokenId = svgstorage.substring(_tokenId, 0, 30);
}

uint256 _vtokenId = uint256(keccak256(hashed)); // totally predictable value.
vandal[vandalId].nft = nft;
vandal[vandalId].tokenId = _tokenId;
vandal[vandalId].tag = tag;
vandal[vandalId].status = 0;
vandal[vandalId].trait = randomNum(_vtokenId);

_safeMint(msg.sender, vandalId);
_setTokenURI(vandalId, original_uri); // same tokenId as svg

if (msg.value - initMintPrice > 0) {
payable(msg.sender).transfer(msg.value - initMintPrice); // excess/padding/buffer
}

emit Vandalized(msg.sender, nft, tokenId, vandalId);

return vandalId;
}

function burn(uint256 tokenId, string memory tag) public payable returns (uint256) {
require(_exists(tokenId), "nothing to clean");
require(msg.value >= getCurrentPriceToBurn(), "not enough eth");
require(bytes(tag).length < 30, "tag max len = 30");
require(vandal[tokenId].status == 0, "already burned");
initMintPrice = initMintPrice - ((initMintPrice / 1000) * 1); // Tagging gets more popular!

uint256 _btokenId = uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, tokenId))); // generate new tokenId for burn caller
uint256 vandalId = _vandalId++;

_safeMint(msg.sender, vandalId);

// burn (this nft) metadata setting
vandal[vandalId].nft = vandal[tokenId].nft;
vandal[vandalId].tokenId = vandal[tokenId].tokenId;
vandal[vandalId].tag = tag;
vandal[vandalId].status = 1;
vandal[vandalId].trait = randomNum(_btokenId);

// original mint (burned nft) metadata zeroed
vandal[tokenId].nft = blackhole;
vandal[tokenId].tokenId = "0";
vandal[tokenId].tag = tag;
vandal[tokenId].status = 2;

payable(ownerOf(tokenId)).transfer(msg.value);

emit CleanedUp(msg.sender, tokenId, vandalId);

return vandalId;
}

function randomNum(uint256 seed) public pure returns (uint256) {
return seed % 4;
}

function checkIfTagged(address nft, uint256 tokenId) public view returns (bool) {
bytes memory result = abi.encode(nft, tokenId);
return isTagged[result];
}

function getStatus(uint256 tokenId) public view returns (vTag memory) {
return vandal[tokenId];
}

function getCurrentPriceToMint() public view returns (uint256) {
return initMintPrice + ((initMintPrice / 1000) * 3); // should grow by 0.3% of current total value after each mint
}

function getCurrentPriceToBurn() public view returns (uint256) {
uint256 baseBurn = getCurrentPriceToMint() * 10;
return baseBurn + initMintPrice;
}

function tokenURI(uint256 tokenId) public view override returns (string memory uri) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
// copied-nft
if (vandal[tokenId].status == 0) {
return super.tokenURI(tokenId);
}

// burner-nft burn() caller NFT
if (vandal[tokenId].status == 1) {
string memory svg = svgstorage.bodyB(
ownerOf(tokenId),
tokenId,
vandal[tokenId].tag,
vandal[tokenId].trait,
vandal[tokenId].nft,
vandal[tokenId].tokenId
);
return string(abi.encodePacked("data:image/svg+xml;base64,", Base64.encode(bytes(svg))));
}

// copied-nft transformed by burn call, zeroed data
if (vandal[tokenId].status == 2) {
return ftokenURI(tokenId);
}
}

// after burning, output of this function will be the same as tokenURI()
function ftokenURI(uint256 tokenId) public view returns (string memory) {
string memory svg = svgstorage.bodyF(
vandal[tokenId].nft,
vandal[tokenId].tokenId,
tokenId,
ownerOf(tokenId),
vandal[tokenId].tag,
vandal[tokenId].trait
);
return string(abi.encodePacked("data:image/svg+xml;base64,", Base64.encode(bytes(svg))));
}

function withdrawETH() public {
require(msg.sender == vandal_king, "Not allowed");
vandal_king.transfer(address(this).balance);
}
}
Loading

0 comments on commit 409fc7d

Please sign in to comment.