Skip to content

Commit

Permalink
Update syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
tarrencev committed Aug 9, 2023
1 parent 01abf6e commit 7253022
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 119 deletions.
36 changes: 28 additions & 8 deletions crates/dojo-erc/src/erc721/components.cairo
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
use starknet::ContractAddress;

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct Balances {
amount: felt252,
struct Balance {
#[key]
token: ContractAddress,
#[key]
account: ContractAddress,
amount: felt252,
}

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct Owners {
address: felt252
struct Owner {
#[key]
token: ContractAddress,
#[key]
token_id: felt252,
address: ContractAddress
}

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct TokenApprovals {
address: felt252,
struct TokenApproval {
#[key]
token: ContractAddress,
#[key]
token_id: felt252,
address: ContractAddress,
}

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct OperatorApprovals {
approved: felt252
struct OperatorApproval {
#[key]
token: ContractAddress,
#[key]
owner: ContractAddress,
#[key]
operator: ContractAddress,
approved: bool
}

#[derive(Component, Copy, Drop, Serde, SerdeLen)]
struct TokenUri {
#[key]
token_id: felt252,
uri: felt252
}
86 changes: 25 additions & 61 deletions crates/dojo-erc/src/erc721/erc721.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,18 @@ mod ERC721 {
use traits::{Into, TryInto};
use zeroable::Zeroable;

use dojo::database::query::{
Query, LiteralIntoQuery, TupleSize1IntoQuery, TupleSize2IntoQuery, IntoPartitioned,
IntoPartitionedQuery
};

use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use dojo_erc::erc721::components::{
Balances, OperatorApprovals, Owners, TokenApprovals, TokenUri
};
use dojo_erc::erc721::components::{Balance, OperatorApproval, Owner, TokenApproval, TokenUri};
use dojo_erc::erc721::systems::{
erc721_approve, erc721_set_approval_for_all, erc721_transfer_from
};

#[storage]
struct Storage {
world_address: ContractAddress,
_name: felt252,
_symbol: felt252,
_uri: felt252,
world: IWorldDispatcher,
token_name: felt252,
token_symbol: felt252,
token_uri: felt252,
}

#[event]
Expand Down Expand Up @@ -58,64 +51,58 @@ mod ERC721 {

#[constructor]
fn constructor(
ref self: ContractState, world: ContractAddress, name: felt252, symbol: felt252
ref self: ContractState, world: IWorldDispatcher, name: felt252, symbol: felt252
) {
self.world_address.write(world);
self._name.write(name);
self._symbol.write(symbol);
self.world.write(world);
self.token_name.write(name);
self.token_symbol.write(symbol);
}

#[external(v0)]
fn name(self: @ContractState) -> felt252 {
self._name.read()
self.token_name.read()
}

#[external(v0)]
fn symbol(self: @ContractState) -> felt252 {
self._symbol.read()
self.token_symbol.read()
}

#[external(v0)]
fn uri(self: @ContractState) -> felt252 {
self._uri.read()
self.token_uri.read()
}

#[external(v0)]
fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {
let token = get_contract_address();
let query: Query = (token, (account, )).into_partitioned();
let mut balance_raw = world(self).entity('Balances'.into(), query, 0, 0);
let balance = serde::Serde::<Balances>::deserialize(ref balance_raw).unwrap();
let balance = get !(self.world.read(), (token, account), Balance);
balance.amount.into()
}

#[external(v0)]
fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress {
let token = get_contract_address();
let query: Query = (token, (u256_into_felt252(token_id), )).into_partitioned();
let mut owner_raw = world(self).entity('Owners'.into(), query, 0, 0);
let owner = serde::Serde::<Owners>::deserialize(ref owner_raw).unwrap();
owner.address.try_into().unwrap()
let owner = get !(self.world.read(), (token, u256_into_felt252(token_id)), Owner);
owner.address
}

#[external(v0)]
fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress {
let token = get_contract_address();
let query: Query = (token, (u256_into_felt252(token_id), )).into_partitioned();
let mut approved_raw = world(self).entity('TokenApprovals'.into(), query, 0, 0);
let approved = serde::Serde::<TokenApprovals>::deserialize(ref approved_raw).unwrap();
approved.address.try_into().unwrap()
let approved = get !(
self.world.read(), (token, u256_into_felt252(token_id)), TokenApproval
);
approved.address
}

#[external(v0)]
fn is_approved_for_all(
self: @ContractState, owner: ContractAddress, operator: ContractAddress
) -> bool {
let token = get_contract_address();
let query: Query = (token, (owner, operator)).into_partitioned();
let mut result_raw = world(self).entity('OperatorApprovals'.into(), query, 0, 0);
let result = serde::Serde::<OperatorApprovals>::deserialize(ref result_raw).unwrap();
felt252_into_bool(result.approved)
let result = get !(self.world.read(), (token, owner, operator), OperatorApproval);
result.approved
}

#[external(v0)]
Expand All @@ -124,7 +111,7 @@ mod ERC721 {
let owner = owner_of(@self, token_id);
calldata.append(u256_into_felt252(token_id));
calldata.append(to.into());
world(@self).execute('erc721_approve'.into(), calldata.span());
self.world.read().execute('erc721_approve'.into(), calldata.span());
let owner = owner_of(@self, token_id);
self.emit(Approval { owner, to, token_id });
}
Expand All @@ -136,8 +123,8 @@ mod ERC721 {
assert(owner != operator, 'ERC721: approval to owner');
calldata.append(owner.into());
calldata.append(operator.into());
calldata.append(bool_into_felt252(approved));
world(@self).execute('erc721_set_approval_for_all'.into(), calldata.span());
calldata.append(approved.into());
self.world.read().execute('erc721_set_approval_for_all'.into(), calldata.span());
self.emit(ApprovalForAll { owner, operator, approved });
}

Expand All @@ -150,35 +137,12 @@ mod ERC721 {
calldata.append(from.into());
calldata.append(to.into());
calldata.append(u256_into_felt252(token_id));
world(@self).execute('erc721_transfer_from'.into(), calldata.span());
self.world.read().execute('erc721_transfer_from'.into(), calldata.span());
self.emit(Transfer { from, to, token_id });
}

// NOTE: temporary, until we have inline commands outside of systems
fn world(self: @ContractState) -> IWorldDispatcher {
IWorldDispatcher { contract_address: self.world_address.read() }
}

fn u256_into_felt252(val: u256) -> felt252 {
// temporary, until TryInto of this is in corelib
val.low.into() + val.high.into() * 0x100000000000000000000000000000000
}


fn bool_into_felt252(_bool: bool) -> felt252 {
if _bool == true {
return 1;
} else {
return 0;
}
}

fn felt252_into_bool(bool_felt252: felt252) -> bool {
if bool_felt252 == 1 {
true
} else {
false
}
}
}

109 changes: 59 additions & 50 deletions crates/dojo-erc/src/erc721/systems.cairo
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
#[system]
mod erc721_approve {
use starknet::{ContractAddress, get_caller_address};
use starknet::ContractAddress;
use traits::Into;

use dojo::world::Context;
use dojo_erc::erc721::components::{Owners, TokenApprovals, OperatorApprovals};
use dojo_erc::erc721::components::{Owner, TokenApproval, OperatorApproval};
use dojo_erc::erc721::erc721::ERC721;

fn execute(ctx: Context, token_id: felt252, operator: felt252) {
let owner = get!(ctx.world, token_id.into(), Owners);
let is_approved_for_all = get!(
ctx.world, (owner.address, operator).into_partitioned(), OperatorApprovals
);
assert(
owner.address == operator || ERC721::felt252_into_bool(is_approved_for_all.approved),
'ERC721: unauthorized caller'
);
set!(
ctx.world,
(token_id, operator).into_partitioned(),
(TokenApprovals { address: operator })
)
fn execute(ctx: Context, token: ContractAddress, token_id: felt252, operator: ContractAddress) {
// TODO: operator + token can not be used defined
let owner = get !(ctx.world, (token, token_id), Owner);
let approval = get !(ctx.world, (token, owner.address, operator), OperatorApproval);
assert(owner.address == operator || approval.approved, 'ERC721: unauthorized caller');
set !(ctx.world, TokenApproval { token, token_id, address: operator });
}
}

Expand All @@ -29,75 +22,91 @@ mod erc721_set_approval_for_all {
use traits::Into;

use dojo::world::Context;
use dojo_erc::erc721::components::OperatorApprovals;
use dojo_erc::erc721::components::OperatorApproval;

fn execute(ctx: Context, owner: felt252, operator: felt252, _approved: felt252) {
fn execute(
ctx: Context,
token: ContractAddress,
owner: ContractAddress,
operator: ContractAddress,
approved: bool
) {
assert(owner != operator, 'ERC721: self approval');
set!(
ctx.world,
(owner, operator).into_partitioned(),
(OperatorApprovals { approved: _approved })
)
set !(ctx.world, OperatorApproval { token, owner, operator, approved });
}
}

#[system]
mod erc721_transfer_from {
use starknet::ContractAddress;
use traits::Into;
use zeroable::Zeroable;

use dojo::world::Context;
use dojo_erc::erc721::components::{Balances, TokenApprovals, OperatorApprovals, Owners};
use dojo_erc::erc721::components::{Balance, TokenApproval, OperatorApproval, Owner};
use dojo_erc::erc721::erc721::ERC721;

fn execute(ctx: Context, from: felt252, to: felt252, token_id: felt252) {
let owner = get!(ctx.world, token_id.into(), Owners);
let is_approved_for_all = get!(
ctx.world, (owner.address, from).into_partitioned(), OperatorApprovals
);
assert(
owner.address == from || ERC721::felt252_into_bool(is_approved_for_all.approved),
'ERC721: unauthorized caller'
);
fn execute(
ctx: Context,
token: ContractAddress,
from: ContractAddress,
to: ContractAddress,
token_id: felt252
) {
let owner = get !(ctx.world, (token, token_id), Owner);
let is_approved = get !(ctx.world, (token, owner.address, from), OperatorApproval);
assert(owner.address == from || is_approved.approved, 'ERC721: unauthorized caller');
assert(!to.is_zero(), 'ERC721: invalid receiver');
assert(from == owner.address, 'ERC721: wrong sender');
set!(ctx.world, (token_id).into(), (TokenApprovals { address: Zeroable::zero() }))
let from_balance = get!(ctx.world, from.into(), Balances);
let to_balance = get!(ctx.world, to.into(), Balances);
set!(ctx.world, (from).into(), (Balances { amount: from_balance.amount - 1 }))
set!(ctx.world, (to).into(), (Balances { amount: to_balance.amount + 1 }))

set !(ctx.world, TokenApproval { token, token_id, address: Zeroable::zero() });

let mut from_balance = get !(ctx.world, (token, from), Balance);
from_balance.amount -= 1;
set !(ctx.world, (from_balance));

let mut to_balance = get !(ctx.world, (token, to), Balance);
to_balance.amount += 1;
set !(ctx.world, (to_balance));
}
}

#[system]
mod erc721_mint {
use starknet::ContractAddress;
use traits::Into;
use zeroable::Zeroable;

use dojo::world::Context;
use dojo_erc::erc721::components::{Balances, Owners};
use dojo_erc::erc721::components::{Balance, Owner};

fn execute(ctx: Context, token_id: felt252, recipient: felt252) {
fn execute(
ctx: Context, token: ContractAddress, token_id: felt252, recipient: ContractAddress
) {
assert(recipient.is_non_zero(), 'ERC721: mint to 0');

// increase token supply
let balance = get!(ctx.world, recipient.into(), Balances);
set!(ctx.world, recipient.into(), (Balances { amount: balance.amount + 1 }));

set!(ctx.world, token_id.into(), (Owners { address: recipient }));
let mut balance = get !(ctx.world, (token, recipient), Balance);
balance.amount += 1;
set !(ctx.world, (balance));
set !(ctx.world, Owner { token, token_id, address: recipient });
}
}

#[system]
mod erc721_burn {
use starknet::ContractAddress;
use traits::Into;
use zeroable::Zeroable;

use dojo::world::Context;
use dojo_erc::erc721::components::{Balances, Owners};
use dojo_erc::erc721::components::{Balance, Owner};

fn execute(ctx: Context, token_id: felt252) {
let owner = get!(ctx.world, token_id.into(), Owners);
let balance = get!(ctx.world, owner.address.into(), Balances);
set!(ctx.world, owner.address.into(), (Balances { amount: balance.amount - 1 }));
set!(ctx.world, token_id.into(), (Owners { address: Zeroable::zero() }));
fn execute(ctx: Context, token: ContractAddress, token_id: felt252) {
let owner = get !(ctx.world, (token, token_id), Owner);
let mut balance = get !(ctx.world, (token, owner.address), Balance);
balance.amount -= 1;
set !(ctx.world, (balance));
set !(ctx.world, Owner { token, token_id, address: Zeroable::zero() });
}
}

0 comments on commit 7253022

Please sign in to comment.