-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: added ERC721 contract * apply requested changes * change variable name
- Loading branch information
Showing
5 changed files
with
269 additions
and
0 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
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,75 @@ | ||
use starknet::ContractAddress; | ||
|
||
#[starknet::interface] | ||
pub trait ILyricsFlipNFT<TContractState> { | ||
fn mint(ref self: TContractState, recipient: ContractAddress); | ||
} | ||
|
||
#[starknet::contract] | ||
mod LyricsFlipNFT { | ||
use openzeppelin::access::ownable::OwnableComponent; | ||
use openzeppelin::introspection::src5::SRC5Component; | ||
use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; | ||
use starknet::storage::StoragePointerReadAccess; | ||
use starknet::storage::StoragePointerWriteAccess; | ||
use starknet::{ContractAddress}; | ||
|
||
component!(path: ERC721Component, storage: erc721, event: ERC721Event); | ||
component!(path: SRC5Component, storage: src5, event: SRC5Event); | ||
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); | ||
|
||
// External | ||
#[abi(embed_v0)] | ||
impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl<ContractState>; | ||
#[abi(embed_v0)] | ||
impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>; | ||
|
||
// Internal | ||
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>; | ||
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>; | ||
|
||
#[storage] | ||
struct Storage { | ||
#[substorage(v0)] | ||
erc721: ERC721Component::Storage, | ||
#[substorage(v0)] | ||
src5: SRC5Component::Storage, | ||
#[substorage(v0)] | ||
ownable: OwnableComponent::Storage, | ||
token_count: u256, | ||
} | ||
|
||
#[event] | ||
#[derive(Drop, starknet::Event)] | ||
enum Event { | ||
#[flat] | ||
ERC721Event: ERC721Component::Event, | ||
#[flat] | ||
SRC5Event: SRC5Component::Event, | ||
#[flat] | ||
OwnableEvent: OwnableComponent::Event, | ||
} | ||
|
||
#[constructor] | ||
fn constructor( | ||
ref self: ContractState, | ||
owner: ContractAddress, | ||
token_name: ByteArray, | ||
token_symbol: ByteArray, | ||
base_uri: ByteArray | ||
) { | ||
self.erc721.initializer(token_name, token_symbol, base_uri); | ||
self.ownable.initializer(owner); | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl ILyricsFlipNFTImpl of super::ILyricsFlipNFT<ContractState> { | ||
fn mint(ref self: ContractState, recipient: ContractAddress) { | ||
let mut token_id = self.token_count.read() + 1; | ||
self.ownable.assert_only_owner(); | ||
assert(!self.erc721.exists(token_id), 'NFT with id already exists'); | ||
self.erc721.mint(recipient, token_id); | ||
self.token_count.write(token_id); | ||
} | ||
} | ||
} |
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,72 @@ | ||
use core::array::ArrayTrait; | ||
use core::byte_array::ByteArray; | ||
use core::result::ResultTrait; | ||
use core::traits::Into; | ||
use lyricsflip::contracts::lyricsflipNFT::{ | ||
ILyricsFlipNFTDispatcher as NFTDispatcher, ILyricsFlipNFTDispatcherTrait as NFTDispatcherTrait | ||
}; | ||
use openzeppelin::token::erc721::interface::{IERC721Dispatcher, IERC721DispatcherTrait}; | ||
use snforge_std::{ | ||
declare, ContractClassTrait, DeclareResultTrait, start_cheat_caller_address, | ||
stop_cheat_caller_address | ||
}; | ||
use starknet::{ContractAddress, contract_address_const}; | ||
|
||
// Account functions | ||
fn owner() -> ContractAddress { | ||
contract_address_const::<'OWNER'>() | ||
} | ||
|
||
fn caller() -> ContractAddress { | ||
contract_address_const::<'CALLER'>() | ||
} | ||
|
||
fn setup_dispatcher() -> (ContractAddress, NFTDispatcher) { | ||
// Declare the contract | ||
let contract = declare("LyricsFlipNFT").unwrap().contract_class(); | ||
|
||
// Prepare constructor calldata | ||
let mut calldata: Array<felt252> = ArrayTrait::new(); | ||
|
||
// Add constructor arguments | ||
calldata.append(owner().into()); | ||
|
||
let name: ByteArray = "TestNFT"; | ||
let symbol: ByteArray = "LYNFT"; | ||
let base_uri: ByteArray = "baseuri"; | ||
|
||
name.serialize(ref calldata); | ||
symbol.serialize(ref calldata); | ||
base_uri.serialize(ref calldata); | ||
|
||
// Deploy contract | ||
let (address, _) = contract.deploy(@calldata).unwrap(); | ||
|
||
// Create dispatcher | ||
(address, NFTDispatcher { contract_address: address }) | ||
} | ||
|
||
#[test] | ||
fn test_successful_mint() { | ||
let (contract_address, dispatcher) = setup_dispatcher(); | ||
let recipient = contract_address_const::<'RECIPIENT'>(); | ||
|
||
start_cheat_caller_address(contract_address, owner()); | ||
dispatcher.mint(recipient); | ||
stop_cheat_caller_address(contract_address); | ||
|
||
let erc721 = IERC721Dispatcher { contract_address }; | ||
assert(erc721.owner_of(1) == recipient, 'Wrong owner'); | ||
assert(erc721.balance_of(recipient) == 1, 'Wrong balance'); | ||
} | ||
|
||
#[test] | ||
#[should_panic(expected: ('Caller is not the owner',))] | ||
fn test_mint_not_owner() { | ||
let (contract_address, dispatcher) = setup_dispatcher(); | ||
let recipient = contract_address_const::<'RECIPIENT'>(); | ||
|
||
start_cheat_caller_address(contract_address, caller()); | ||
dispatcher.mint(recipient); | ||
stop_cheat_caller_address(contract_address); | ||
} |