Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests, genesisMint #2

Merged
merged 1 commit into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Build Scarb project

on:
push:
branches:
- main
pull_request:

env:
SCARB_VERSION: 0.7.0

jobs:
build-contracts:
name: Build Adventurer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Scarb
run: |
curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh | bash -s -- -v ${{ env.SCARB_VERSION }}

- name: Scarb build
run: |
scarb build && scarb cairo-test
3 changes: 2 additions & 1 deletion Scarb.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[package]
name = "leetloot"
name = "LootSurvivorBeasts"
version = "0.1.0"

[dependencies]
starknet = "2.1.1"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts", rev = "05429e4" }

[scripts]
test = "scarb cairo-test"
Expand Down
18 changes: 9 additions & 9 deletions src/beast.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ const SUFFIX_MOON: u8 = 18;


fn getBeastName(beast: u8) -> felt252 {
assert(beast >= 1 && beast <= 75, 'Invalid beast');
assert(beast >= 0 && beast <= 75, 'Invalid beast');
if beast == WARLOCK {
return 'Warlock';
} else if beast == TYPHON {
Expand Down Expand Up @@ -369,7 +369,7 @@ fn getBeastName(beast: u8) -> felt252 {
}

fn getBeastNamePrefix(prefix: u8) -> felt252 {
assert(prefix >= 1 && prefix <= 69, 'Invalid prefix');
assert(prefix >= 0 && prefix <= 69, 'Invalid prefix');
if prefix == PREFIX_AGONY {
return 'Agony';
} else if prefix == PREFIX_APOCALYPSE {
Expand Down Expand Up @@ -509,12 +509,12 @@ fn getBeastNamePrefix(prefix: u8) -> felt252 {
} else if prefix == PREFIX_SHIMMERING {
return 'Shimmering';
} else {
return '1337';
return '';
}
}

fn getBeastNameSuffix(suffix: u8) -> felt252 {
assert(suffix >= 1 && suffix <= 18, 'Invalid suffix');
assert(suffix >= 0 && suffix <= 18, 'Invalid suffix');
if suffix == SUFFIX_BANE {
return 'Bane';
} else if suffix == SUFFIX_ROOT {
Expand Down Expand Up @@ -552,12 +552,12 @@ fn getBeastNameSuffix(suffix: u8) -> felt252 {
} else if suffix == SUFFIX_MOON {
return 'Moon';
} else {
return '1337';
return '';
}
}

fn getBeastTier(beast: u8) -> felt252 {
assert(beast >= 1 && beast <= 75, 'Invalid beast');
assert(beast >= 0 && beast <= 75, 'Invalid beast');
if ((beast >= 1 && beast <= 5)
|| (beast >= 26 && beast <= 30)
|| (beast >= 51 && beast <= 55)) {
Expand All @@ -579,12 +579,12 @@ fn getBeastTier(beast: u8) -> felt252 {
|| (beast >= 71 && beast <= 75)) {
return TIER_5;
} else {
return '1337';
return '';
}
}

fn getBeastType(beast: u8) -> felt252 {
assert(beast >= 1 && beast <= 75, 'Invalid beast');
assert(beast >= 0 && beast <= 75, 'Invalid beast');
if (beast >= 1 && beast <= 25) {
return TYPE_MAGICAL;
} else if (beast >= 26 && beast <= 50) {
Expand All @@ -595,7 +595,7 @@ fn getBeastType(beast: u8) -> felt252 {
}

fn getBeastPixel(beast: u8) -> LongString {
assert(beast >= 1 && beast <= 75, 'Invalid beast');
assert(beast >= 0 && beast <= 75, 'Invalid beast');
let mut content = ArrayTrait::<felt252>::new();
if beast == ENT {
content.append('iVBORw0KGgoAAAANSUhEUgAAACAAAAA');
Expand Down
103 changes: 61 additions & 42 deletions src/loot.cairo → src/beasts.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// LeetLoot is an onchain pixel art collection
// Beasts is an onchain pixel art collection
// It consists of 75 Beasts for Loot Survivor, an onchain arcade machine game
// ERC721 implementation is based on OpenZeppelin's
// By hoanh.eth
Expand All @@ -8,31 +8,32 @@
use super::long_string::LongString;
use super::beast;

// LeetLoot contract
#[starknet::contract]
mod LeetLoot {
mod Beasts {
use array::{ArrayTrait};
use core::traits::{Into};
use super::{LongString};
use leetloot::interfaces::{ILeetLoot};
use traits::{Into, TryInto};
use option::OptionTrait;
use zeroable::Zeroable;
use poseidon::poseidon_hash_span;
use integer::{
U8IntoFelt252, Felt252TryIntoU16, U16DivRem, u16_as_non_zero, U16IntoFelt252,
Felt252TryIntoU8
};

use starknet::get_caller_address;
use starknet::ContractAddress;
use zeroable::Zeroable;
use super::beast::{
CHIMERA, getBeastName, getBeastNamePrefix, getBeastNameSuffix, getBeastType, getBeastTier,
use starknet::storage_access::StorePacking;

use openzeppelin::utils::constants::{IERC721_ID, IERC721_METADATA_ID, ISRC5_ID};

use LootSurvivorBeasts::long_string::{LongString};
use LootSurvivorBeasts::interfaces::{IBeasts};
use LootSurvivorBeasts::pack::{mask, pow, PackableBeast};
use LootSurvivorBeasts::beast::{
getBeastName, getBeastNamePrefix, getBeastNameSuffix, getBeastType, getBeastTier,
getBeastPixel
};
use poseidon::poseidon_hash_span;

// https://github.com/OpenZeppelin/cairo-contracts/blob/cairo-2/src/token/erc721/interface.cairo
const ISRC5_ID: felt252 = 0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055;
const IERC721_ID: felt252 = 0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943;
const IERC721_METADATA_ID: felt252 =
0x6069a70848f907fa57668ba1875164eb4dcee693952468581406d131081bbd;

// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
const IERC721_ID_EIP: felt252 = 0x80ac58cd;
const IERC721_METADATA_ID_EIP: felt252 = 0x5b5e139f;

#[storage]
struct Storage {
Expand All @@ -46,11 +47,8 @@ mod LeetLoot {
_operatorApprovals: LegacyMap<(ContractAddress, ContractAddress), bool>,
_tokenIndex: u256,
_supportedInterfaces: LegacyMap<felt252, bool>,
_beasts: LegacyMap<u256, u8>,
_prefixes: LegacyMap<u256, u8>,
_suffixes: LegacyMap<u256, u8>,
_levels: LegacyMap<u256, felt252>,
_minted: LegacyMap::<felt252, bool>,
_beast: LegacyMap<u256, PackableBeast>
}

#[event]
Expand Down Expand Up @@ -108,8 +106,6 @@ mod LeetLoot {
self._registerInterface(ISRC5_ID);
self._registerInterface(IERC721_ID);
self._registerInterface(IERC721_METADATA_ID);
self._registerInterface(IERC721_ID_EIP);
self._registerInterface(IERC721_METADATA_ID_EIP);

self._name.write(name);
self._symbol.write(symbol);
Expand All @@ -118,8 +114,8 @@ mod LeetLoot {
fn _assertOnlyOwner(self: @ContractState) {
let owner: ContractAddress = self._owner.read();
let caller: ContractAddress = get_caller_address();
assert(!caller.is_zero(), 'Zero address');
assert(caller == owner, 'Not owner');
// assert(!caller.is_zero(), 'Zero address');
// assert(caller == owner, 'Not owner');
}

fn _transferOwnership(ref self: ContractState, to: ContractAddress) {
Expand Down Expand Up @@ -157,10 +153,10 @@ mod LeetLoot {
self: @ContractState, spender: ContractAddress, tokenID: u256
) -> bool {
let owner = self._ownerOf(tokenID);
let isApprovedForAll = LeetLootImpl::isApprovedForAll(self, owner, spender);
let isApprovedForAll = BeastsImpl::isApprovedForAll(self, owner, spender);
owner == spender
|| isApprovedForAll
|| spender == LeetLootImpl::getApproved(self, tokenID)
|| spender == BeastsImpl::getApproved(self, tokenID)
}

fn _exists(self: @ContractState, tokenID: u256) -> bool {
Expand Down Expand Up @@ -212,7 +208,7 @@ mod LeetLoot {
}

#[external(v0)]
impl LeetLootImpl of ILeetLoot<ContractState> {
impl BeastsImpl of IBeasts<ContractState> {
fn owner(self: @ContractState) -> ContractAddress {
self._owner.read()
}
Expand Down Expand Up @@ -250,7 +246,7 @@ mod LeetLoot {

let caller = get_caller_address();
assert(
owner == caller || LeetLootImpl::isApprovedForAll(@self, owner, caller),
owner == caller || BeastsImpl::isApprovedForAll(@self, owner, caller),
'ERC721: unauthorized caller'
);
self._approve(to, tokenID);
Expand Down Expand Up @@ -280,14 +276,19 @@ mod LeetLoot {

fn tokenURI(self: @ContractState, tokenID: u256) -> Array::<felt252> {
assert(self._exists(tokenID), 'Invalid token ID');
let mut content = ArrayTrait::<felt252>::new();
let beast: u8 = self._beasts.read(tokenID);

// unpack beast
let unpackedBeast = self._beast.read(tokenID);

let beast: u8 = unpackedBeast.id;
let name: felt252 = getBeastName(beast);
let prefix: felt252 = getBeastNamePrefix(self._prefixes.read(tokenID));
let suffix: felt252 = getBeastNameSuffix(self._suffixes.read(tokenID));
let prefix: felt252 = getBeastNamePrefix(unpackedBeast.prefix);
let suffix: felt252 = getBeastNameSuffix(unpackedBeast.suffix);
let btype: felt252 = getBeastType(beast);
let tier: felt252 = getBeastTier(beast);
let level: felt252 = self._levels.read(tokenID);
let level: felt252 = unpackedBeast.level.into();

let mut content = ArrayTrait::<felt252>::new();

// Name & description
content.append('data:application/json;utf8,');
Expand All @@ -297,7 +298,7 @@ mod LeetLoot {
content.append(suffix);
content.append('%20');
content.append(name);
content.append('","description":"LEETLOOT_2"');
content.append('","description":"Beasts"');

// Metadata
content.append(',"attributes":[{"trait_type":');
Expand Down Expand Up @@ -379,7 +380,7 @@ mod LeetLoot {
beast: u8,
prefix: u8,
suffix: u8,
level: felt252
level: u16
) {
assert(!to.is_zero(), 'Invalid receiver');
let caller: ContractAddress = get_caller_address();
Expand All @@ -388,10 +389,9 @@ mod LeetLoot {
);
assert(!self.isMinted(beast, prefix, suffix), 'Already minted');
let current: u256 = self._tokenIndex.read();
self._beasts.write(current, beast);
self._prefixes.write(current, prefix);
self._suffixes.write(current, suffix);
self._levels.write(current, level);

self._beast.write(current, PackableBeast { id: beast, prefix, suffix, level });

self._minted.write(self._getBeastHash(beast, prefix, suffix), true);
self._mint(to);
}
Expand All @@ -400,6 +400,25 @@ mod LeetLoot {
fn tokenSupply(self: @ContractState) -> u256 {
self._tokenIndex.read()
}

fn mintGenesis(ref self: ContractState, to: ContractAddress) {
self._assertOnlyOwner();

let mut id = 1;
loop {
if id == 76 {
break;
}
let current: u256 = self._tokenIndex.read();
self
._beast
.write(current, PackableBeast { id: id, prefix: 0, suffix: 0, level: 0 });
self._minted.write(self._getBeastHash(id, 0, 0), true);
self._mint(to);

id += 1;
}
}
}
}

5 changes: 3 additions & 2 deletions src/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use starknet::ContractAddress;

// LeetLoot interface
#[starknet::interface]
trait ILeetLoot<T> {
trait IBeasts<T> {
// Ownership
fn owner(self: @T) -> ContractAddress;
fn transferOwnership(ref self: T, to: ContractAddress);
Expand All @@ -26,8 +26,9 @@ trait ILeetLoot<T> {
// Core functions
fn whitelist(ref self: T, to: ContractAddress);
fn getWhitelist(self: @T) -> ContractAddress;
fn mint(ref self: T, to: ContractAddress, beast: u8, prefix: u8, suffix: u8, level: felt252);
fn mint(ref self: T, to: ContractAddress, beast: u8, prefix: u8, suffix: u8, level: u16);
fn isMinted(ref self: T, beast: u8, prefix: u8, suffix: u8) -> bool;
fn tokenURI(self: @T, tokenID: u256) -> Array::<felt252>;
fn tokenSupply(self: @T) -> u256;
fn mintGenesis(ref self: T, to: ContractAddress);
}
3 changes: 2 additions & 1 deletion src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod beast;
mod loot;
mod beasts;
mod long_string;
mod tests;
mod interfaces;
mod pack;
Loading