Skip to content

Commit

Permalink
feat: account v3 tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Darlington02 committed Sep 18, 2024
1 parent 8f095b0 commit b272e97
Show file tree
Hide file tree
Showing 11 changed files with 181 additions and 230 deletions.
8 changes: 4 additions & 4 deletions src/accountV3/accountV3.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ pub mod AccountV3 {
if (get_caller_address() == _token_contract
&& token_id == _token_id
&& tx_info.chain_id == _chain_id) {
panic!("Account: ownership cycle!");
panic(array!['Account: ownership cycle!']);
}

return 0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc;
}

/// @notice retrieves deployment details of an account
fn get_context(self: @ContractState) -> (ContractAddress, felt252, felt252) {
fn context(self: @ContractState) -> (ContractAddress, felt252, felt252) {
self.account._context()
}
}
Expand Down Expand Up @@ -191,9 +191,9 @@ pub mod AccountV3 {
/// @notice replaces the contract's class hash with `new_class_hash`.
/// Emits an `Upgraded` event.
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
// validate signer
// validate signer is owner
let caller = get_caller_address();
assert(self.is_valid_signer(caller), Errors::UNAUTHORIZED);
assert(self.signatory._base_signer_validation(caller), Errors::UNAUTHORIZED);

// cannot make this call when the account is lock
let (is_locked, _) = self.lockable.is_locked();
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IAccountV3.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ pub trait IAccountV3<TContractState> {
token_id: u256,
data: Span<felt252>
) -> felt252;
fn get_context(self: @TContractState) -> (ContractAddress, felt252, felt252);
fn context(self: @TContractState) -> (ContractAddress, felt252, felt252);
}
1 change: 1 addition & 0 deletions src/interfaces/IERC721.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub trait IERC721<TContractState> {
token_id: u256,
data: Span<felt252>
);
fn mint(ref self: TContractState, to: ContractAddress, token_id: u256);
fn approve(ref self: TContractState, to: ContractAddress, token_id: u256);
fn set_approval_for_all(ref self: TContractState, operator: ContractAddress, approved: bool);
fn get_approved(self: @TContractState, token_id: u256) -> ContractAddress;
Expand Down
194 changes: 22 additions & 172 deletions src/test_helper/erc721_helper.cairo
Original file line number Diff line number Diff line change
@@ -1,200 +1,50 @@
use starknet::ContractAddress;

#[starknet::interface]
pub trait IERC721<TContractState> {
fn balance_of(self: @TContractState, account: ContractAddress) -> u256;
fn ownerOf(self: @TContractState, token_id: u256) -> ContractAddress;
fn owner_of(self: @TContractState, token_id: u256) -> ContractAddress;
fn transfer_from(
ref self: TContractState, from: ContractAddress, to: ContractAddress, token_id: u256
);
fn approve(ref self: TContractState, to: ContractAddress, token_id: u256);
fn set_approval_for_all(ref self: TContractState, operator: ContractAddress, approved: bool);
fn get_approved(self: @TContractState, token_id: u256) -> ContractAddress;
fn is_approved_for_all(
self: @TContractState, owner: ContractAddress, operator: ContractAddress
) -> bool;
// IERC721Metadata
fn name(self: @TContractState) -> felt252;
fn symbol(self: @TContractState) -> felt252;
fn token_uri(self: @TContractState, token_id: u256) -> felt252;
// Internal
trait IERC721<TContractState> {
fn mint(ref self: TContractState, to: ContractAddress, token_id: u256);
}

#[starknet::contract]
pub mod ERC721 {
use starknet::storage::StorageMapWriteAccess;
use starknet::storage::StorageMapReadAccess;
use starknet::storage::StoragePointerReadAccess;
use starknet::storage::StoragePointerWriteAccess;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};
use starknet::ContractAddress;
use starknet::get_caller_address;
use core::num::traits::zero::Zero;
use starknet::{storage::Map};

component!(path: ERC721Component, storage: erc721, event: ERC721Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// ERC721 Mixin
#[abi(embed_v0)]
impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl<ContractState>;
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
name: felt252,
symbol: felt252,
owners: Map::<u256, ContractAddress>,
balances: Map::<ContractAddress, u256>,
token_approvals: Map::<u256, ContractAddress>,
operator_approvals: Map::<(ContractAddress, ContractAddress), bool>,
token_uri: Map<u256, felt252>,
#[substorage(v0)]
erc721: ERC721Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
Approval: Approval,
Transfer: Transfer,
ApprovalForAll: ApprovalForAll
}

#[derive(Drop, starknet::Event)]
struct Approval {
owner: ContractAddress,
to: ContractAddress,
token_id: u256
}

#[derive(Drop, starknet::Event)]
struct Transfer {
from: ContractAddress,
to: ContractAddress,
token_id: u256
}

#[derive(Drop, starknet::Event)]
struct ApprovalForAll {
owner: ContractAddress,
operator: ContractAddress,
approved: bool
#[flat]
ERC721Event: ERC721Component::Event,
#[flat]
SRC5Event: SRC5Component::Event
}

#[constructor]
fn constructor(ref self: ContractState, _name: felt252, _symbol: felt252) {
self.name.write(_name);
self.symbol.write(_symbol);
fn constructor(ref self: ContractState, name: felt252, symbol: felt252) {
self.erc721.initializer("tokenbound", "TBA", "https://api.example.com/v1/");
}

#[abi(embed_v0)]
impl ERC721Impl of super::IERC721<ContractState> {
fn name(self: @ContractState) -> felt252 {
self.name.read()
}

fn symbol(self: @ContractState) -> felt252 {
self.symbol.read()
}

fn ownerOf(self: @ContractState, token_id: u256) -> ContractAddress {
let owner = self.owners.read(token_id);
assert(owner.is_non_zero(), 'ERC721: invalid token ID');
owner
}

fn owner_of(self: @ContractState, token_id: u256) -> ContractAddress {
let owner = self.owners.read(token_id);
assert(owner.is_non_zero(), 'ERC721: invalid token ID');
owner
}

fn token_uri(self: @ContractState, token_id: u256) -> felt252 {
self.token_uri.read(token_id)
}

fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {
assert(account.is_non_zero(), 'ERC721: address zero');
self.balances.read(account)
}

fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress {
assert(self._exists(token_id), 'ERC721: invalid token ID');
self.token_approvals.read(token_id)
}

fn is_approved_for_all(
self: @ContractState, owner: ContractAddress, operator: ContractAddress
) -> bool {
self.operator_approvals.read((owner, operator))
}

fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) {
let owner = self.ownerOf(token_id);
assert(to != owner, 'Approval to current owner');
assert(
get_caller_address() == owner
|| self.is_approved_for_all(owner, get_caller_address()),
'Not token owner'
);
self.token_approvals.write(token_id, to);
self.emit(Approval { owner: self.ownerOf(token_id), to: to, token_id: token_id });
}

fn set_approval_for_all(
ref self: ContractState, operator: ContractAddress, approved: bool
) {
let owner = get_caller_address();
assert(owner != operator, 'ERC721: approve to caller');
self.operator_approvals.write((owner, operator), approved);
self.emit(ApprovalForAll { owner: owner, operator: operator, approved: approved });
}

fn transfer_from(
ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256
) {
assert(
self._is_approved_or_owner(get_caller_address(), token_id),
'neither owner nor approved'
);
self._transfer(from, to, token_id);
}

fn mint(ref self: ContractState, to: ContractAddress, token_id: u256) {
assert(to.is_non_zero(), 'to is zero address');

let receiver_balance = self.balances.read(to);
self.balances.write(to, receiver_balance + 1.into());
self.owners.write(token_id, to);

self.emit(Transfer { from: Zero::zero(), to: to, token_id: token_id });
}
}

#[generate_trait]
impl ERC721HelperImpl of ERC721HelperTrait {
fn _exists(self: @ContractState, token_id: u256) -> bool {
self.ownerOf(token_id).is_non_zero()
}

fn _is_approved_or_owner(
self: @ContractState, spender: ContractAddress, token_id: u256
) -> bool {
let owner = self.owners.read(token_id);
spender == owner
|| self.is_approved_for_all(owner, spender)
|| self.get_approved(token_id) == spender
}

fn _set_token_uri(ref self: ContractState, token_id: u256, token_uri: felt252) {
assert(self._exists(token_id), 'ERC721: invalid token ID');
self.token_uri.write(token_id, token_uri)
}

fn _transfer(
ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256
) {
assert(from == self.ownerOf(token_id), 'ERC721: Caller is not owner');
assert(to.is_non_zero(), 'ERC721: transfer to 0 address');

self.token_approvals.write(token_id, Zero::zero());
self.balances.write(from, self.balances.read(from) - 1.into());
self.balances.write(to, self.balances.read(to) + 1.into());
self.owners.write(token_id, to);

self.emit(Transfer { from: from, to: to, token_id: token_id });
self.erc721.mint(to, token_id);
}
}
}
9 changes: 4 additions & 5 deletions tests/test_account_component.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ use core::hash::HashStateTrait;
use core::pedersen::PedersenTrait;

use token_bound_accounts::interfaces::IRegistry::{IRegistryDispatcherTrait, IRegistryDispatcher};
use token_bound_accounts::interfaces::IAccount::{
IAccountDispatcher, IAccountDispatcherTrait, IAccountSafeDispatcher, IAccountSafeDispatcherTrait
};
use token_bound_accounts::interfaces::IAccount::{IAccountDispatcher, IAccountDispatcherTrait};
use token_bound_accounts::interfaces::IExecutable::{
IExecutableDispatcher, IExecutableDispatcherTrait
};
use token_bound_accounts::interfaces::IERC721::{IERC721Dispatcher, IERC721DispatcherTrait};
use token_bound_accounts::components::presets::account_preset::AccountPreset;
use token_bound_accounts::components::account::account::AccountComponent;
use token_bound_accounts::registry::registry::Registry;

use token_bound_accounts::test_helper::{
hello_starknet::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, HelloStarknet},
erc721_helper::{IERC721Dispatcher, IERC721DispatcherTrait, ERC721},
simple_account::{ISimpleAccountDispatcher, ISimpleAccountDispatcherTrait, SimpleAccount}
simple_account::{ISimpleAccountDispatcher, ISimpleAccountDispatcherTrait, SimpleAccount},
erc721_helper::ERC721
};

const ACCOUNT: felt252 = 1234;
Expand Down
Loading

0 comments on commit b272e97

Please sign in to comment.