Skip to content

Commit

Permalink
Merge pull request #2 from hoanhan101/loaf
Browse files Browse the repository at this point in the history
tests, genesisMint
  • Loading branch information
ponderingdemocritus authored Aug 29, 2023
2 parents da60fb4 + 7cb9be1 commit a4a39a6
Show file tree
Hide file tree
Showing 8 changed files with 632 additions and 94 deletions.
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

0 comments on commit a4a39a6

Please sign in to comment.