diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index 52ed25191..f238e9c6f 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -60,12 +60,19 @@ Returns the short string `'VALID'` if valid, otherwise it reverts. :OwnerAdded: xref:Account-OwnerAdded[OwnerAdded] :OwnerRemoved: xref:Account-OwnerRemoved[OwnerRemoved] +:dual-interfaces: xref:interfaces.adoc#dual_interfaces[Dual Interfaces] ```javascript use openzeppelin::account::Account; ``` Account contract implementation extending xref:ISRC6[`ISRC6`]. +[.contract-index] +.Constructor +-- +* xref:#Account-constructor[`++constructor(self, _public_key)++`] +-- + [.contract-index] .Utilities -- @@ -75,8 +82,6 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. [.contract-index] .External Functions -- -* xref:#Account-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] - [.contract-subindex-inherited] .SRC6Impl @@ -95,17 +100,24 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. * xref:#Account-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] [.contract-subindex-inherited] -.PublicKeyImpl +.Non-Standard +* xref:#Account-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] * xref:#Account-set_public_key[`++set_public_key(self, new_public_key)++`] * xref:#Account-get_public_key[`++get_public_key(self)++`] + +[.contract-subindex-inherited] +.Account Camel + +* xref:#Account-isValidSignature[`++isValidSignature(self, hash, signature)++`] +* xref:#Account-supportsInterface[`++supportsInterface(self, interfaceId)++`] +* xref:#Account-setPublicKey[`++setPublicKey(self, newPublicKey)++`] +* xref:#Account-getPublicKey[`++getPublicKey(self)++`] -- [.contract-index] .Internal Functions -- -* xref:#Account-constructor[`++constructor(self, _public_key)++`] - [.contract-subindex-inherited] .InternalImpl @@ -122,17 +134,8 @@ Account contract implementation extending xref:ISRC6[`ISRC6`]. * xref:#Account-OwnerRemoved[`++OwnerRemoved(removed_owner_guid)++`] -- -[#Account-Utilities] -==== Utilities - -[.contract-item] -[[Account-assert_only_self]] -==== `[.contract-item-name]#++assert_only_self++#++(self: @ContractState)++` [.item-kind]#internal# - -Validates that the caller is the account itself. Otherwise it reverts. - -[#Account-Functions] -==== Functions +[#Account-Constructor] +==== Constructor [.contract-item] [[Account-constructor]] @@ -142,14 +145,17 @@ Initializes the account with the given public key, and registers the ISRC6 inter Emits an {OwnerAdded} event. +[#Account-Utilities] +==== Utilities + [.contract-item] -[[Account-__validate_deploy__]] -==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252) → felt252++` [.item-kind]#external# +[[Account-assert_only_self]] +==== `[.contract-item-name]#++assert_only_self++#++(self: @ContractState)++` [.item-kind]#internal# -Validates a https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/Blocks/transactions/#deploy_account_transaction[`DeployAccount` transaction]. -See xref:/guides/deployment.adoc[Counterfactual Deployments]. +Validates that the caller is the account itself. Otherwise it reverts. -Returns the short string `'VALID'` if valid, otherwise it reverts. +[#Account-External-Functions] +==== External Functions [.contract-item] [[Account-__execute__]] @@ -183,6 +189,15 @@ Validates a https://docs.starknet.io/documentation/architecture_and_concepts/Net Returns the short string `'VALID'` if valid, otherwise it reverts. +[.contract-item] +[[Account-__validate_deploy__]] +==== `[.contract-item-name]#++__validate_deploy__++#++(self: @ContractState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252) → felt252++` [.item-kind]#external# + +Validates a https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/Blocks/transactions/#deploy_account_transaction[`DeployAccount` transaction]. +See xref:/guides/deployment.adoc[Counterfactual Deployments]. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + [.contract-item] [[Account-set_public_key]] ==== `[.contract-item-name]#++set_public_key++#++(ref self: ContractState, new_public_key: felt252)++` [.item-kind]#external# @@ -197,6 +212,41 @@ Emits both an {OwnerRemoved} and an {OwnerAdded} event. Returns the current public key of the account. +[.contract-item] +[[Account-isValidSignature]] +==== `[.contract-item-name]#++isValidSignature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# + +See xref:ISRC6-is_valid_signature[ISRC6::is_valid_signature]. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed in {dual-interfaces}. + +[.contract-item] +[[Account-supportsInterface]] +==== `[.contract-item-name]#++supportsInterface++#++(self: @ContractState, interfaceId: felt252) → bool++` [.item-kind]#external# + +See xref:Account-supports_interface[Account::supports_interface]. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed in {dual-interfaces}. + +[.contract-item] +[[Account-setPublicKey]] +==== `[.contract-item-name]#++setPublicKey++#++(ref self: ContractState, newPublicKey: felt252)++` [.item-kind]#external# + +See xref:Account-set_public_key[Account::set_public_key]. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed in {dual-interfaces}. + +[.contract-item] +[[Account-getPublicKey]] +==== `[.contract-item-name]#++getPublicKey++#++(self: @ContractState)++ → felt252` [.item-kind]#external# + +See xref:Account-get_public_key[Account::get_public_key]. + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed in {dual-interfaces}. + +[#Account-Internal-Functions] +==== Internal Functions + [.contract-item] [[Account-initializer]] ==== `[.contract-item-name]#++initializer++#++(ref self: ContractState, _public_key: felt252)++` [.item-kind]#internal# diff --git a/src/account.cairo b/src/account.cairo index 1ca2c1578..e7d4da161 100644 --- a/src/account.cairo +++ b/src/account.cairo @@ -7,5 +7,3 @@ use account::QUERY_VERSION; use account::TRANSACTION_VERSION; use interface::AccountABIDispatcher; use interface::AccountABIDispatcherTrait; -use interface::AccountCamelABIDispatcher; -use interface::AccountCamelABIDispatcherTrait; diff --git a/src/account/account.cairo b/src/account/account.cairo index 13b7e8d0e..99f7901ed 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -116,11 +116,10 @@ mod Account { } #[external(v0)] - impl SRC6CamelOnlyImpl of interface::ISRC6CamelOnly { - fn isValidSignature( - self: @ContractState, hash: felt252, signature: Array - ) -> felt252 { - SRC6Impl::is_valid_signature(self, hash, signature) + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) } } @@ -132,19 +131,13 @@ mod Account { } #[external(v0)] - impl SRC5Impl of ISRC5 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) - } - } - - #[external(v0)] - impl SRC5CamelImpl of ISRC5Camel { - fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = SRC5::unsafe_new_contract_state(); - SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) - } + fn __validate_deploy__( + self: @ContractState, + class_hash: felt252, + contract_address_salt: felt252, + _public_key: felt252 + ) -> felt252 { + self.validate_transaction() } #[external(v0)] @@ -160,6 +153,23 @@ mod Account { } } + #[external(v0)] + impl SRC6CamelOnlyImpl of interface::ISRC6CamelOnly { + fn isValidSignature( + self: @ContractState, hash: felt252, signature: Array + ) -> felt252 { + SRC6Impl::is_valid_signature(self, hash, signature) + } + } + + #[external(v0)] + impl SRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = SRC5::unsafe_new_contract_state(); + SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) + } + } + #[external(v0)] impl PublicKeyCamelImpl of super::PublicKeyCamelTrait { fn getPublicKey(self: @ContractState) -> felt252 { @@ -171,16 +181,6 @@ mod Account { } } - #[external(v0)] - fn __validate_deploy__( - self: @ContractState, - class_hash: felt252, - contract_address_salt: felt252, - _public_key: felt252 - ) -> felt252 { - self.validate_transaction() - } - // // Internal // diff --git a/src/account/interface.cairo b/src/account/interface.cairo index b9a5abbef..c75bb57d0 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -25,31 +25,33 @@ trait IDeclarer { fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; } +// +// Account ABI +// + #[starknet::interface] trait AccountABI { + // ISRC6 fn __execute__(self: @TState, calls: Array) -> Array>; fn __validate__(self: @TState, calls: Array) -> felt252; + fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IDeclarer fn __validate_declare__(self: @TState, class_hash: felt252) -> felt252; + + // Non-Standard fn __validate_deploy__( self: @TState, class_hash: felt252, contract_address_salt: felt252, _public_key: felt252 ) -> felt252; fn set_public_key(ref self: TState, new_public_key: felt252); fn get_public_key(self: @TState) -> felt252; - fn is_valid_signature(self: @TState, hash: felt252, signature: Array) -> felt252; - fn supports_interface(self: @TState, interface_id: felt252) -> bool; -} -// Entry points case-convention is enforced by the protocol -#[starknet::interface] -trait AccountCamelABI { - fn __execute__(self: @TState, calls: Array) -> Array>; - fn __validate__(self: @TState, calls: Array) -> felt252; - fn __validate_declare__(self: @TState, classHash: felt252) -> felt252; - fn __validate_deploy__( - self: @TState, classHash: felt252, contractAddressSalt: felt252, _publicKey: felt252 - ) -> felt252; - fn setPublicKey(ref self: TState, newPublicKey: felt252); - fn getPublicKey(self: @TState) -> felt252; + // Account Camel fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; + fn setPublicKey(ref self: TState, newPublicKey: felt252); + fn getPublicKey(self: @TState) -> felt252; } diff --git a/src/tests/account/test_dual_account.cairo b/src/tests/account/test_dual_account.cairo index 2c2156ed6..29baf030f 100644 --- a/src/tests/account/test_dual_account.cairo +++ b/src/tests/account/test_dual_account.cairo @@ -1,7 +1,5 @@ use openzeppelin::account::AccountABIDispatcher; use openzeppelin::account::AccountABIDispatcherTrait; -use openzeppelin::account::AccountCamelABIDispatcher; -use openzeppelin::account::AccountCamelABIDispatcherTrait; use openzeppelin::account::dual_account::DualCaseAccount; use openzeppelin::account::dual_account::DualCaseAccountABI; use openzeppelin::introspection::interface::ISRC5_ID; @@ -34,12 +32,12 @@ fn setup_snake() -> (DualCaseAccount, AccountABIDispatcher) { ) } -fn setup_camel() -> (DualCaseAccount, AccountCamelABIDispatcher) { +fn setup_camel() -> (DualCaseAccount, AccountABIDispatcher) { let mut calldata = array![PUBLIC_KEY]; let target = utils::deploy(CamelAccountMock::TEST_CLASS_HASH, calldata); ( DualCaseAccount { contract_address: target }, - AccountCamelABIDispatcher { contract_address: target } + AccountABIDispatcher { contract_address: target } ) } diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index df5d1fd25..719cf2a21 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -118,6 +118,20 @@ mod ERC20 { } } + #[external(v0)] + fn increase_allowance( + ref self: ContractState, spender: ContractAddress, added_value: u256 + ) -> bool { + self._increase_allowance(spender, added_value) + } + + #[external(v0)] + fn decrease_allowance( + ref self: ContractState, spender: ContractAddress, subtracted_value: u256 + ) -> bool { + self._decrease_allowance(spender, subtracted_value) + } + #[external(v0)] impl ERC20CamelOnlyImpl of IERC20CamelOnly { fn totalSupply(self: @ContractState) -> u256 { @@ -138,13 +152,6 @@ mod ERC20 { } } - #[external(v0)] - fn increase_allowance( - ref self: ContractState, spender: ContractAddress, added_value: u256 - ) -> bool { - self._increase_allowance(spender, added_value) - } - #[external(v0)] fn increaseAllowance( ref self: ContractState, spender: ContractAddress, addedValue: u256 @@ -152,13 +159,6 @@ mod ERC20 { increase_allowance(ref self, spender, addedValue) } - #[external(v0)] - fn decrease_allowance( - ref self: ContractState, spender: ContractAddress, subtracted_value: u256 - ) -> bool { - self._decrease_allowance(spender, subtracted_value) - } - #[external(v0)] fn decreaseAllowance( ref self: ContractState, spender: ContractAddress, subtractedValue: u256 diff --git a/src/token/erc20/interface.cairo b/src/token/erc20/interface.cairo index 8f0526299..ef830d973 100644 --- a/src/token/erc20/interface.cairo +++ b/src/token/erc20/interface.cairo @@ -41,8 +41,13 @@ trait IERC20CamelOnly { ) -> bool; } +// +// ERC20 ABI +// + #[starknet::interface] trait ERC20ABI { + // IERC20 fn name(self: @TState) -> felt252; fn symbol(self: @TState) -> felt252; fn decimals(self: @TState) -> u8; @@ -54,25 +59,19 @@ trait ERC20ABI { ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) -> bool; fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; + + // Non-Standard fn increase_allowance(ref self: TState, spender: ContractAddress, added_value: u256) -> bool; fn decrease_allowance( ref self: TState, spender: ContractAddress, subtracted_value: u256 ) -> bool; -} -#[starknet::interface] -trait ERC20CamelABI { - fn name(self: @TState) -> felt252; - fn symbol(self: @TState) -> felt252; - fn decimals(self: @TState) -> u8; + // ERC20 Camel fn totalSupply(self: @TState) -> u256; fn balanceOf(self: @TState, account: ContractAddress) -> u256; - fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool; fn transferFrom( ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256 ) -> bool; - fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool; fn increaseAllowance(ref self: TState, spender: ContractAddress, addedValue: u256) -> bool; fn decreaseAllowance(ref self: TState, spender: ContractAddress, subtractedValue: u256) -> bool; } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 559b57c9c..1ba293792 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -89,46 +89,6 @@ mod ERC721 { // External // - #[external(v0)] - impl SRC5Impl of ISRC5 { - fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { - let unsafe_state = src5::SRC5::unsafe_new_contract_state(); - src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) - } - } - - #[external(v0)] - impl SRC5CamelImpl of ISRC5Camel { - fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { - let unsafe_state = src5::SRC5::unsafe_new_contract_state(); - src5::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) - } - } - - #[external(v0)] - impl ERC721MetadataImpl of interface::IERC721Metadata { - fn name(self: @ContractState) -> felt252 { - self._name.read() - } - - fn symbol(self: @ContractState) -> felt252 { - self._symbol.read() - } - - fn token_uri(self: @ContractState, token_id: u256) -> felt252 { - assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self._token_uri.read(token_id) - } - } - - #[external(v0)] - impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { - fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { - assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); - self._token_uri.read(tokenId) - } - } - #[external(v0)] impl ERC721Impl of interface::IERC721 { fn balance_of(self: @ContractState, account: ContractAddress) -> u256 { @@ -140,15 +100,26 @@ mod ERC721 { self._owner_of(token_id) } - fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { - assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); - self._token_approvals.read(token_id) + fn safe_transfer_from( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + assert( + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED + ); + self._safe_transfer(from, to, token_id, data); } - fn is_approved_for_all( - self: @ContractState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - self._operator_approvals.read((owner, operator)) + fn transfer_from( + ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256 + ) { + assert( + self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED + ); + self._transfer(from, to, token_id); } fn approve(ref self: ContractState, to: ContractAddress, token_id: u256) { @@ -168,26 +139,39 @@ mod ERC721 { self._set_approval_for_all(get_caller_address(), operator, 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), Errors::UNAUTHORIZED - ); - self._transfer(from, to, token_id); + fn get_approved(self: @ContractState, token_id: u256) -> ContractAddress { + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); + self._token_approvals.read(token_id) } - fn safe_transfer_from( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - assert( - self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED - ); - self._safe_transfer(from, to, token_id, data); + fn is_approved_for_all( + self: @ContractState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + self._operator_approvals.read((owner, operator)) + } + } + + #[external(v0)] + impl SRC5Impl of ISRC5 { + fn supports_interface(self: @ContractState, interface_id: felt252) -> bool { + let unsafe_state = src5::SRC5::unsafe_new_contract_state(); + src5::SRC5::SRC5Impl::supports_interface(@unsafe_state, interface_id) + } + } + + #[external(v0)] + impl ERC721MetadataImpl of interface::IERC721Metadata { + fn name(self: @ContractState) -> felt252 { + self._name.read() + } + + fn symbol(self: @ContractState) -> felt252 { + self._symbol.read() + } + + fn token_uri(self: @ContractState, token_id: u256) -> felt252 { + assert(self._exists(token_id), Errors::INVALID_TOKEN_ID); + self._token_uri.read(token_id) } } @@ -201,6 +185,26 @@ mod ERC721 { ERC721Impl::owner_of(self, tokenId) } + fn safeTransferFrom( + ref self: ContractState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ) { + ERC721Impl::safe_transfer_from(ref self, from, to, tokenId, data) + } + + fn transferFrom( + ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 + ) { + ERC721Impl::transfer_from(ref self, from, to, tokenId) + } + + fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { + ERC721Impl::set_approval_for_all(ref self, operator, approved) + } + fn getApproved(self: @ContractState, tokenId: u256) -> ContractAddress { ERC721Impl::get_approved(self, tokenId) } @@ -210,25 +214,21 @@ mod ERC721 { ) -> bool { ERC721Impl::is_approved_for_all(self, owner, operator) } + } - fn setApprovalForAll(ref self: ContractState, operator: ContractAddress, approved: bool) { - ERC721Impl::set_approval_for_all(ref self, operator, approved) - } - - fn transferFrom( - ref self: ContractState, from: ContractAddress, to: ContractAddress, tokenId: u256 - ) { - ERC721Impl::transfer_from(ref self, from, to, tokenId) + #[external(v0)] + impl SRC5CamelImpl of ISRC5Camel { + fn supportsInterface(self: @ContractState, interfaceId: felt252) -> bool { + let unsafe_state = src5::SRC5::unsafe_new_contract_state(); + src5::SRC5::SRC5CamelImpl::supportsInterface(@unsafe_state, interfaceId) } + } - fn safeTransferFrom( - ref self: ContractState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ) { - ERC721Impl::safe_transfer_from(ref self, from, to, tokenId, data) + #[external(v0)] + impl ERC721MetadataCamelOnlyImpl of interface::IERC721MetadataCamelOnly { + fn tokenURI(self: @ContractState, tokenId: u256) -> felt252 { + assert(self._exists(tokenId), Errors::INVALID_TOKEN_ID); + self._token_uri.read(tokenId) } } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index 86125721d..024da607b 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -88,3 +88,53 @@ trait IERC721ReceiverCamel { data: Span ) -> felt252; } + +// +// ERC721 ABI +// + +#[starknet::interface] +trait ERC721ABI { + // IERC721 + fn balance_of(self: @TState, account: ContractAddress) -> u256; + fn owner_of(self: @TState, token_id: u256) -> ContractAddress; + fn safe_transfer_from( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(ref self: TState, from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(ref self: TState, to: ContractAddress, token_id: u256); + fn set_approval_for_all(ref self: TState, operator: ContractAddress, approved: bool); + fn get_approved(self: @TState, token_id: u256) -> ContractAddress; + fn is_approved_for_all( + self: @TState, owner: ContractAddress, operator: ContractAddress + ) -> bool; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IERC721Metadata + fn name(self: @TState) -> felt252; + fn symbol(self: @TState) -> felt252; + fn token_uri(self: @TState, token_id: u256) -> felt252; + + // ERC721 Camel + fn balanceOf(self: @TState, account: ContractAddress) -> u256; + fn ownerOf(self: @TState, tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + ref self: TState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(ref self: TState, from: ContractAddress, to: ContractAddress, tokenId: u256); + fn setApprovalForAll(ref self: TState, operator: ContractAddress, approved: bool); + fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; + fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; + fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; + fn tokenURI(self: @TState, tokenId: u256) -> felt252; +}