Skip to content

Commit d397d17

Browse files
committed
chores: Created a custom nft
1 parent d037dac commit d397d17

12 files changed

+247
-11
lines changed

src/lib.cairo

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
pub mod mods;
2+

src/mods.cairo

+15-4
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,26 @@ pub mod types;
22
pub mod events;
33
pub mod errors;
44

5-
pub mod token{
5+
pub mod token {
66
pub mod WeaverNFT;
77
}
88

9-
pub mod interfaces{
9+
pub mod interfaces {
1010
pub mod IWeaver;
1111
pub mod IWeaverNFT;
12+
pub mod ICustom;
1213
}
1314

14-
pub mod weaver_contract{
15+
pub mod weaver_contract {
1516
pub mod weaver;
16-
}
17+
}
18+
19+
pub mod protocol {
20+
pub mod protocol_component;
21+
pub mod protocolNFT;
22+
}
23+
24+
pub mod Utils{
25+
pub mod Convert_felt_to_bytearray;
26+
}
27+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
pub impl FeltTryIntoByteArray of TryInto<felt252, ByteArray> {
3+
fn try_into(self: felt252) -> Option<ByteArray> {
4+
let mut res: ByteArray = "";
5+
let mut length = 0;
6+
let mut data: u256 = self.into();
7+
loop {
8+
if data == 0 {
9+
break;
10+
}
11+
data /= 0x100;
12+
length += 1;
13+
};
14+
15+
res.append_word(self, length);
16+
Option::Some(res)
17+
}
18+
}
19+
20+
21+
22+
pub fn convert_into_byteArray(ref svg: Array<felt252>) -> ByteArray {
23+
let mut res: ByteArray = Default::default();
24+
// converting felt252 array to byte array
25+
while (!svg.is_empty()) {
26+
let each_felt: felt252 = svg.pop_front().unwrap();
27+
let word: ByteArray = each_felt.try_into().unwrap();
28+
res.append(@word);
29+
};
30+
res
31+
}
32+
33+
34+
#[cfg(test)]
35+
36+
mod test {
37+
use super::FeltTryIntoByteArray;
38+
39+
#[test]
40+
fn from_felt_to_bytearray() {
41+
let a = 'PROTOCOL NFT NAME';
42+
let b: ByteArray = a.try_into().unwrap();
43+
assert(b == "PROTOCOL NFT NAME", 'INVAILD_NFT_NAME');
44+
}
45+
}
46+

src/mods/errors.cairo

+3
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ pub mod Errors{
88
pub const UNAUTHORIZED: felt252 = 'UNAUTHORIZED';
99
pub const INVALID_ADDRESS: felt252 = 'INVALID_ADDRESS';
1010
pub const CLASS_HASH_CANNOT_BE_ZERO: felt252 = 'CLASS_HASH_CANNOT_BE_ZERO';
11+
pub const ALREADY_MINTED: felt252 = 'ALREADY_MINTED';
12+
pub const INVALID_TOKEN_ID: felt252 = 'INVALID_TOKEN_ID';
13+
pub const TOKEN_NOT_EXISTS: felt252 = 'TOKEN_NOT_EXISTS';
1114
}

src/mods/interfaces/ICustom.cairo

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use starknet::ContractAddress;
2+
use starknet::class_hash::ClassHash;
3+
4+
// *************************************************************************
5+
// INTERFACE of WEAVER
6+
// *************************************************************************
7+
8+
9+
#[starknet::interface]
10+
pub trait ICustom<TContractState>{
11+
fn mint_nft(ref self: TContractState, user_address: ContractAddress) -> u256;
12+
fn burn_nft(ref self: TContractState, user_address: ContractAddress, token_id: u256);
13+
fn get_user_token_id(self: @TContractState, user: ContractAddress) -> u256;
14+
fn name(self: @TContractState) -> ByteArray;
15+
fn symbol(self: @TContractState)-> ByteArray;
16+
fn token_uri(self: @TContractState, token_id: u256) -> ByteArray;
17+
18+
}

src/mods/interfaces/IWeaver.cairo

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use starknet::class_hash::ClassHash;
55
// INTERFACE of WEAVER
66
// *************************************************************************
77

8-
98
use crate::mods::types::{ProtocolInfo, TaskInfo, User};
109

1110
#[starknet::interface]

src/mods/interfaces/IWeaverNFT.cairo

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ pub trait IWeaverNFT<TState> {
88
fn get_last_minted_id(self: @TState) -> u256;
99
fn get_user_token_id(self: @TState, user: ContractAddress) -> u256;
1010
fn get_token_mint_timestamp(self: @TState, token_id: u256) -> u64;
11+
1112
}

src/mods/protocol/protocolNFT.cairo

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#[starknet::contract]
2+
pub mod protocolNFT {
3+
// *************************************************************************
4+
// IMPORTS
5+
// *************************************************************************
6+
7+
use openzeppelin_token::erc721::interface::IERC721Metadata;
8+
use ERC721Component::InternalTrait;
9+
use openzeppelin_token::erc721::interface::ERC721ABI;
10+
use starknet::{ContractAddress, get_block_timestamp};
11+
use core::num::traits::zero::Zero;
12+
use openzeppelin_introspection::src5::SRC5Component;
13+
use openzeppelin_token::erc721::{ERC721Component, ERC721HooksEmptyImpl};
14+
use openzeppelin_access::ownable::OwnableComponent;
15+
use openzeppelin_upgrades::UpgradeableComponent;
16+
17+
18+
use crate::mods::interfaces::ICustom::ICustom;
19+
use crate::mods::errors::Errors;
20+
use crate::mods::Utils::Convert_felt_to_bytearray::convert_into_byteArray;
21+
22+
23+
use starknet::storage::{
24+
Map, StoragePointerWriteAccess, StoragePointerReadAccess, StorageMapReadAccess,
25+
StorageMapWriteAccess
26+
};
27+
28+
29+
// *************************************************************************
30+
// COMPONENTS
31+
// *************************************************************************
32+
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
33+
component!(path: ERC721Component, storage: erc721, event: ERC721Event);
34+
component!(path: SRC5Component, storage: src5, event: SRC5Event);
35+
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
36+
37+
// ERC721 Mixin
38+
impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl<ContractState>;
39+
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>;
40+
41+
// add an owner
42+
#[abi(embed_v0)]
43+
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
44+
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
45+
46+
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;
47+
48+
// *************************************************************************
49+
// STORAGE
50+
// *************************************************************************
51+
#[storage]
52+
struct Storage {
53+
#[substorage(v0)]
54+
erc721: ERC721Component::Storage,
55+
#[substorage(v0)]
56+
src5: SRC5Component::Storage,
57+
#[substorage(v0)]
58+
ownable: OwnableComponent::Storage,
59+
#[substorage(v0)]
60+
upgradeable: UpgradeableComponent::Storage,
61+
last_minted_id: u256,
62+
mint_timestamp: Map<u256, u64>,
63+
user_token_id: Map<ContractAddress, u256>,
64+
protocol_id: u256
65+
}
66+
67+
// *************************************************************************
68+
// EVENTS
69+
// *************************************************************************
70+
#[event]
71+
#[derive(Drop, starknet::Event)]
72+
enum Event {
73+
#[flat]
74+
ERC721Event: ERC721Component::Event,
75+
#[flat]
76+
SRC5Event: SRC5Component::Event,
77+
#[flat]
78+
OwnableEvent: OwnableComponent::Event,
79+
#[flat]
80+
UpgradeableEvent: UpgradeableComponent::Event
81+
}
82+
83+
// *************************************************************************
84+
// CONSTRUCTOR
85+
// *************************************************************************
86+
#[constructor]
87+
fn constructor(ref self: ContractState, protocol_id: u256, admin: ContractAddress) {
88+
self.ownable.initializer(admin);
89+
self.protocol_id.write(protocol_id);
90+
}
91+
92+
93+
#[abi(embed_v0)]
94+
impl IprotocolNFT of ICustom<ContractState> {
95+
fn mint_nft(ref self: ContractState, user_address: ContractAddress) -> u256 {
96+
let balance = self.erc721.balance_of(user_address);
97+
assert(balance.is_zero(), Errors::ALREADY_MINTED);
98+
let token_id = self.last_minted_id.read() + 1;
99+
self.erc721.mint(user_address, token_id);
100+
let timestamp: u64 = get_block_timestamp();
101+
self.user_token_id.write(user_address, token_id);
102+
self.last_minted_id.write(token_id);
103+
self.mint_timestamp.write(token_id, timestamp);
104+
return token_id;
105+
}
106+
107+
108+
fn burn_nft(ref self: ContractState, user_address: ContractAddress, token_id: u256) {
109+
let user_token_id = self.user_token_id.read(user_address);
110+
assert(user_token_id == token_id, Errors::INVALID_TOKEN_ID);
111+
assert(self.erc721.exists(token_id), Errors::TOKEN_NOT_EXISTS);
112+
self.erc721.burn(token_id);
113+
self.user_token_id.write(user_address, 0);
114+
}
115+
116+
117+
// *************************************************************************
118+
// GETTERS
119+
// *************************************************************************
120+
121+
fn get_user_token_id(self: @ContractState, user: ContractAddress) -> u256 {
122+
return self.user_token_id.read(user);
123+
}
124+
125+
126+
// *************************************************************************
127+
// METADATA
128+
// *************************************************************************
129+
130+
131+
fn name(self: @ContractState) -> ByteArray {
132+
let mut collection_name = ArrayTrait::<felt252>::new();
133+
let protocol_id_felt252: felt252 = self.protocol_id.read().try_into().unwrap();
134+
collection_name.append('PROTOCOL');
135+
collection_name.append(protocol_id_felt252);
136+
let protocol_name_byte = convert_into_byteArray(ref collection_name);
137+
138+
return protocol_name_byte;
139+
140+
}
141+
142+
143+
fn symbol(self: @ContractState)-> ByteArray {
144+
return "";
145+
}
146+
147+
148+
fn token_uri(self: @ContractState, token_id: u256) -> ByteArray {
149+
let token_uri = IERC721Metadata::token_uri(self.erc721, token_id);
150+
return token_uri;
151+
}
152+
}
153+
}
154+
155+
156+
157+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/mods/types.cairo

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ pub struct TaskInfo {
1515
#[derive(Drop, Serde, Debug, PartialEq, starknet::Store)]
1616
pub struct ProtocolInfo {
1717
pub protocol_name: ByteArray,
18-
}
18+
}

src/mods/weaver_contract/weaver.cairo

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub mod Weaver {
2020
use crate::mods::events::{ProtocolRegistered, TaskMinted, Upgraded, UserRegistered};
2121
use crate::mods::errors::Errors;
2222
use crate::mods::interfaces::IWeaver::IWeaver;
23-
use crate::mods::interfaces::IWeaverNFT::{IWeaverNFTDispatcher,IWeaverNFTDispatcherTrait};
23+
use crate::mods::interfaces::IWeaverNFT::{IWeaverNFTDispatcher, IWeaverNFTDispatcherTrait};
2424

2525
// *************************************************************************
2626
// STORAGE
@@ -52,8 +52,6 @@ pub mod Weaver {
5252
}
5353

5454

55-
56-
5755
#[constructor]
5856
fn constructor(ref self: ContractState, owner: ContractAddress) {
5957
self.owner.write(owner);

tests/test_weaver_contract.cairo

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ use snforge_std::{
1111
use starknet::{ContractAddress, get_block_timestamp};
1212

1313
use weaver_contract::mods::interfaces::IWeaver::{IWeaverDispatcher, IWeaverDispatcherTrait};
14-
use weaver_contract::mods::interfaces::IWeaverNFT::{IWeaverNFTDispatcher, IWeaverNFTDispatcherTrait};
14+
use weaver_contract::mods::interfaces::IWeaverNFT::{
15+
IWeaverNFTDispatcher, IWeaverNFTDispatcherTrait
16+
};
1517
use weaver_contract::mods::events::{UserRegistered, ProtocolRegistered, TaskMinted};
1618
use weaver_contract::mods::weaver_contract::weaver::Weaver::{Event};
1719

1820

19-
2021
fn OWNER() -> ContractAddress {
2122
'owner'.try_into().unwrap()
2223
}

0 commit comments

Comments
 (0)