Skip to content

Commit 22b686f

Browse files
authored
ERC-721 example: fix panic re: approve_for with nonexistent token (#2092)
* ERC-721 example: fix panic re: `approve_for` with nonexistent token * Update changelog * Refactor token_transfer_from
1 parent d21cc6a commit 22b686f

File tree

2 files changed

+35
-32
lines changed

2 files changed

+35
-32
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020

2121
### Fixed
2222
- Fix the `StorageVec` type by excluding the `len_cached` field from its type info - [#2052](https://github.com/paritytech/ink/pull/2052)
23+
- Fix panic in `approve_for` in the ERC-721 example - [#2092](https://github.com/paritytech/ink/pull/2092)
2324
- ERC-721: `transfer_token_from` now ensures the token owner is correct - [#2093](https://github.com/paritytech/ink/pull/2093)
2425

2526
## Version 5.0.0-rc

integration-tests/erc721/lib.rs

+34-32
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ mod erc721 {
217217

218218
let owner = token_owner.get(id).ok_or(Error::TokenNotFound)?;
219219
if owner != caller {
220-
return Err(Error::NotOwner)
220+
return Err(Error::NotOwner);
221221
};
222222

223223
let count = owned_tokens_count
@@ -244,14 +244,12 @@ mod erc721 {
244244
id: TokenId,
245245
) -> Result<(), Error> {
246246
let caller = self.env().caller();
247-
if !self.exists(id) {
248-
return Err(Error::TokenNotFound)
247+
let owner = self.owner_of(id).ok_or(Error::TokenNotFound)?;
248+
if !self.approved_or_owner(caller, id, owner) {
249+
return Err(Error::NotApproved);
249250
};
250-
if !self.approved_or_owner(Some(caller), id) {
251-
return Err(Error::NotApproved)
252-
};
253-
if self.token_owner.get(id) != Some(*from) {
254-
return Err(Error::NotOwner)
251+
if owner != *from {
252+
return Err(Error::NotOwner);
255253
};
256254
self.clear_approval(id);
257255
self.remove_token_from(from, id)?;
@@ -277,7 +275,7 @@ mod erc721 {
277275
} = self;
278276

279277
if !token_owner.contains(id) {
280-
return Err(Error::TokenNotFound)
278+
return Err(Error::TokenNotFound);
281279
}
282280

283281
let count = owned_tokens_count
@@ -299,11 +297,11 @@ mod erc721 {
299297
} = self;
300298

301299
if token_owner.contains(id) {
302-
return Err(Error::TokenExists)
300+
return Err(Error::TokenExists);
303301
}
304302

305303
if *to == AccountId::from([0x0; 32]) {
306-
return Err(Error::NotAllowed)
304+
return Err(Error::NotAllowed);
307305
};
308306

309307
let count = owned_tokens_count
@@ -325,7 +323,7 @@ mod erc721 {
325323
) -> Result<(), Error> {
326324
let caller = self.env().caller();
327325
if to == caller {
328-
return Err(Error::NotAllowed)
326+
return Err(Error::NotAllowed);
329327
}
330328
self.env().emit_event(ApprovalForAll {
331329
owner: caller,
@@ -346,19 +344,17 @@ mod erc721 {
346344
/// the message's sender.
347345
fn approve_for(&mut self, to: &AccountId, id: TokenId) -> Result<(), Error> {
348346
let caller = self.env().caller();
349-
let owner = self.owner_of(id);
350-
if !(owner == Some(caller)
351-
|| self.approved_for_all(owner.expect("Error with AccountId"), caller))
352-
{
353-
return Err(Error::NotAllowed)
347+
let owner = self.owner_of(id).ok_or(Error::TokenNotFound)?;
348+
if !(owner == caller || self.approved_for_all(owner, caller)) {
349+
return Err(Error::NotAllowed);
354350
};
355351

356352
if *to == AccountId::from([0x0; 32]) {
357-
return Err(Error::NotAllowed)
353+
return Err(Error::NotAllowed);
358354
};
359355

360356
if self.token_approvals.contains(id) {
361-
return Err(Error::CannotInsert)
357+
return Err(Error::CannotInsert);
362358
} else {
363359
self.token_approvals.insert(id, to);
364360
}
@@ -389,20 +385,16 @@ mod erc721 {
389385

390386
/// Returns true if the `AccountId` `from` is the owner of token `id`
391387
/// or it has been approved on behalf of the token `id` owner.
392-
fn approved_or_owner(&self, from: Option<AccountId>, id: TokenId) -> bool {
393-
let owner = self.owner_of(id);
394-
from != Some(AccountId::from([0x0; 32]))
388+
fn approved_or_owner(
389+
&self,
390+
from: AccountId,
391+
id: TokenId,
392+
owner: AccountId,
393+
) -> bool {
394+
from != AccountId::from([0x0; 32])
395395
&& (from == owner
396-
|| from == self.token_approvals.get(id)
397-
|| self.approved_for_all(
398-
owner.expect("Error with AccountId"),
399-
from.expect("Error with AccountId"),
400-
))
401-
}
402-
403-
/// Returns true if token `id` exists or false if it does not.
404-
fn exists(&self, id: TokenId) -> bool {
405-
self.token_owner.contains(id)
396+
|| self.token_approvals.get(id) == Some(from)
397+
|| self.approved_for_all(owner, from))
406398
}
407399
}
408400

@@ -563,6 +555,16 @@ mod erc721 {
563555
assert!(!erc721.is_approved_for_all(accounts.alice, accounts.bob));
564556
}
565557

558+
#[ink::test]
559+
fn approve_nonexistent_token_should_fail() {
560+
let accounts =
561+
ink::env::test::default_accounts::<ink::env::DefaultEnvironment>();
562+
// Create a new contract instance.
563+
let mut erc721 = Erc721::new();
564+
// Approve transfer of nonexistent token id 1
565+
assert_eq!(erc721.approve(accounts.bob, 1), Err(Error::TokenNotFound));
566+
}
567+
566568
#[ink::test]
567569
fn not_approved_transfer_should_fail() {
568570
let accounts =

0 commit comments

Comments
 (0)