Summary
erc721_metadata()
uses the wrong Cairo type when returning a value from a match
expression.
Vulnerability Details
erc721_metadata()
returns Option<ERC721Metadata>
, Option
, so it can use it in a match
expression in every call to erc721_metadata()
. ERC721Metadata
consists of 4 ByteArray
fields:
#[derive(Drop)]
struct ERC721Metadata {
name: ByteArray,
symbol: ByteArray,
base_uri: ByteArray,
uris: Span<ByteArray>,
}
The function loops through all the token_ids
and then tries to get the tokenURI
of each one. If the token has it is retrieved via token_uri_from_contract_call()
, this returns a ByteArray
because these values are then stored in ERC721Metadata.uris
, which is a Span<ByteArray>
. But if the tokenId
does not have a tokenURI
, token_uri_from_contract_call()
will return Option::None
and the match
expression will add ""
to the ERC721Metadata.uris
, which is not of type ByteArray
.
When ERC721Metadata
is returned, the base_uri
is also set to ""
, which again is not a ByteArray
.
fn erc721_metadata(
contract_address: ContractAddress,
token_ids: Option<Span<u256>>
) -> Option<ERC721Metadata> {
let erc721 = IERC721Dispatcher { contract_address };
let uris = match token_ids {
Option::Some(ids) => {
let mut out_uris = array![];
let mut i = 0_usize;
loop {
if i == ids.len() {
break ();
}
let token_id = *ids[i];
let token_uri =
match token_uri_from_contract_call(erc721.contract_address, token_id) {
Option::Some(uri) => uri,
Option::None => "",
};
out_uris.append(token_uri);
i += 1;
};
out_uris.span()
},
Option::None => {
array![].span()
}
};
Option::Some(
ERC721Metadata {
name: erc721.name(),
symbol: erc721.symbol(),
base_uri: "",
uris
}
)
}
Impact
The wrong Cairo type is passed when the code expects a ByteArray
.
Tools Used
Manual Review
Recommendations
fn erc721_metadata(
contract_address: ContractAddress,
token_ids: Option<Span<u256>>
) -> Option<ERC721Metadata> {
let erc721 = IERC721Dispatcher { contract_address };
let uris = match token_ids {
Option::Some(ids) => {
let mut out_uris = array![]; // array ByteArray
let mut i = 0_usize;
loop {
if i == ids.len() {
break ();
}
let token_id = *ids[i];
let token_uri =
match token_uri_from_contract_call(erc721.contract_address, token_id) {
Option::Some(uri) => uri,
- Option::None => "",
+ Option::None => ByteArray::new(),
};
out_uris.append(token_uri);
i += 1;
};
out_uris.span() // Span<ByteArray>
},
Option::None => {
array![].span()
}
};
Option::Some(
ERC721Metadata {
name: erc721.name(),
symbol: erc721.symbol(),
- base_uri: "",
+ base_uri: ByteArray::new(),
uris
}
)
}