import { AccountId , AccountId0 , Bool , msg , ScaleString , SpreadStorableArray , SpreadStorableMap , u128 , UInt128 } from "ask-lang" ; @ storage class ERC721Storage { _holderTokens : SpreadStorableMap < AccountId , SpreadStorableArray < UInt128 > > ; _tokenOwners : SpreadStorableMap < UInt128 , AccountId > ; _tokenApprovals : SpreadStorableMap < UInt128 , AccountId > ; _operatorApprovals : SpreadStorableMap < AccountId , SpreadStorableMap < AccountId , Bool > > ; _name : string ; _symbol : string ; _tokenURIs : SpreadStorableMap < UInt128 , ScaleString > ; _baseURI : string ; } dev @ event class Transfer { @ topic from : AccountId = AccountId0 ; @ topic to : AccountId = AccountId0 ; @ topic tokenId : u128 = u128 . Zero ; constructor ( from , to , tokenId ) { this . from = from ; this . to = to ; this . tokenId = tokenId ; } } ; dev @ event class Approval { @ topic owner : AccountId ; @ topic approved : AccountId ; @ topic tokenId : u128 ; constructor ( owner , approved , tokenId ) { this . owner = owner ; this . approved = approved ; this . tokenId = tokenId ; } } dev @ event class ApprovalForAll { @ topic owner : AccountId ; @ topic operator : AccountId ; approved : bool ; constructor ( owner , operator , approved ) { this . owner = owner ; this . operator = operator ; this . approved = approved ; } } @ contract export class ERC721 { storage : ERC721Storage ; constructor ( ) { this . storage = new ERC721Storage ( ) ; } private _exists ( tokenId ) { let id = new UInt128 ( tokenId ) ; return this . storage . _tokenOwners . has ( id ) ; } @ constructor default ( name = "" , symbol = "" ) { this . storage . _name = name ; this . storage . _symbol = symbol ; } @ message ( mutates = false ) balanceOf ( owner ) { assert ( owner . notEq ( AccountId0 ) , "ERC721: balance query for the zero address" ) ; return this . storage . _holderTokens . get ( owner ) . length ; } dev @ message ( mutates = false ) ownerOf ( tokenId ) { assert ( this . _exists ( tokenId ) , "ERC721: owner query for nonexistent token" ) ; return this . storage . _tokenOwners . get ( new UInt128 ( tokenId ) ) ; } dev @ message ( mutates = false ) name ( ) { return this . storage . _name ; } dev @ message ( mutates = false ) symbol ( ) { return this . storage . _symbol ; } dev @ message ( mutates = false ) tokenURI ( tokenId ) { assert ( this . _exists ( tokenId ) , "ERC721Metadata: URI query for nonexistent token" ) ; let id = new UInt128 ( tokenId ) ; let tokenURI = this . storage . _tokenURIs . get ( id ) . toString ( ) ; if ( this . storage . _baseURI . length == 0 ) return tokenURI ; if ( tokenURI . length > 0 ) return this . storage . _baseURI + tokenURI ; return this . storage . _baseURI + id . toString ( ) ; } dev @ message ( mutates = false ) baseURI ( ) { return this . storage . _baseURI ; } dev @ message ( mutates = false ) tokenOfOwnerByIndex ( owner , index ) { return this . storage . _holderTokens . get ( owner ) . at ( index ) . unwrap ( ) ; } dev @ message ( mutates = false ) totalSupply ( ) { return this . storage . _tokenOwners . keys ( ) . length ; } dev @ message ( mutates = false ) tokenByIndex ( index ) { return this . storage . _tokenOwners . keys ( ) . at ( index ) . unwrap ( ) ; } dev @ message approve ( to , tokenId ) { let owner = this . ownerOf ( tokenId ) ; assert ( to . notEq ( owner ) , "ERC721: approval to current owner" ) ; assert ( msg . sender . eq ( owner ) || this . isApprovedForAll ( owner , msg . sender ) , "ERC721: approve caller is not owner nor approved for all" ) ; this . _approve ( to , tokenId ) ; } dev @ message ( mutates = false ) getApproved ( tokenId ) { assert ( this . _exists ( tokenId ) , "ERC721: approved query for nonexistent token" ) ; return this . storage . _tokenApprovals . get ( new UInt128 ( tokenId ) ) ; } dev private _getOperatorApprovals ( operator ) { let approvals = this . storage . _operatorApprovals . get ( operator ) ; if ( approvals . entryKey == "" ) { let key = this . storage . _operatorApprovals . entryKey + operator . toString ( ) ; approvals = new SpreadStorableMap < AccountId , Bool > ( key ) ; this . storage . _operatorApprovals . set ( operator , approvals ) ; } return approvals ; } @ message setApprovalForAll ( operator , approved ) { assert ( operator . notEq ( msg . sender ) , "ERC721: approve to caller" ) ; let approvals = this . _getOperatorApprovals ( msg . sender ) ; approvals . set ( operator , new Bool ( approved ) ) ; ( new ApprovalForAll ( msg . sender , operator , approved ) ) ; } dev @ message ( mutates = false ) isApprovedForAll ( owner , operator ) { return this . storage . _operatorApprovals . get ( owner ) . get ( operator ) . unwrap ( ) ; } dev @ message transferFrom ( from , to , tokenId ) { let isApproved = this . _isApprovedOrOwner ( msg . sender , tokenId ) ; assert ( isApproved , "ERC721: transfer caller is not owner nor approved" ) ; this . _transfer ( from , to , tokenId ) ; } dev @ message safeTransferFrom ( from , to , tokenId , _data ) { let isApproved = this . _isApprovedOrOwner ( msg . sender , tokenId ) ; assert ( isApproved , "ERC721: transfer caller is not owner nor approved" ) ; this . _safeTransfer ( from , to , tokenId , _data ) ; } protected _safeTransfer ( from , to , tokenId , _data ) { this . _transfer ( from , to , tokenId ) ; } dev protected _isApprovedOrOwner ( spender , tokenId ) { assert ( this . _exists ( tokenId ) , "ERC721: operator query for nonexistent token" ) ; let owner = this . ownerOf ( tokenId ) ; return ( spender . eq ( owner ) || this . getApproved ( tokenId ) . eq ( spender ) || this . isApprovedForAll ( owner , spender ) ) ; } dev protected _safeMint ( to , tokenId , data ) { this . _mint ( to , tokenId ) ; } dev protected _mint ( to , tokenId ) { assert ( to . notEq ( AccountId0 ) , "ERC721: mint to the zero address" ) ; assert ( ! this . _exists ( tokenId ) , "ERC721: token already minted" ) ; this . _getHolderTokens ( to ) . push ( new UInt128 ( tokenId ) ) ; this . storage . _tokenOwners . set ( new UInt128 ( tokenId ) , to ) ; ( new Transfer ( AccountId0 , to , tokenId ) ) ; } dev protected _burn ( tokenId ) { let owner = this . ownerOf ( tokenId ) ; this . _approve ( AccountId0 , tokenId ) ; let tid = new UInt128 ( tokenId ) ; let uri = this . storage . _tokenURIs . get ( tid ) . toString ( ) ; if ( uri . length != 0 ) { this . storage . _tokenURIs . delete ( tid ) ; } let tokensOfOwner = this . _getHolderTokens ( owner ) ; for ( let i = 0 ; i < tokensOfOwner . length ; i ++ ) { if ( tokensOfOwner [ i ] . eq ( tid ) ) { tokensOfOwner . delete ( i ) ; break ; } } this . storage . _tokenOwners . delete ( tid ) ; ( new Transfer ( owner , AccountId0 , tokenId ) ) ; } dev private _getHolderTokens ( to ) { let list = this . storage . _holderTokens . get ( to ) ; if ( list . entryKey == "" ) { let key = this . storage . _holderTokens . entryKey + to . toString ( ) ; list = new SpreadStorableArray < UInt128 > ( key ) ; this . storage . _holderTokens . set ( to , list ) ; } return list ; } protected _transfer ( from , to , tokenId ) { assert ( this . ownerOf ( tokenId ) . eq ( from ) , "ERC721: transfer of token that is not own" ) ; assert ( to . notEq ( AccountId0 ) , "ERC721: transfer to the zero address" ) ; this . _approve ( AccountId0 , tokenId ) ; let tid = new UInt128 ( tokenId ) ; let tokensOfOwner = this . _getHolderTokens ( from ) ; for ( let i = 0 ; i < tokensOfOwner . length ; i ++ ) { if ( tokensOfOwner [ i ] . eq ( tid ) ) { tokensOfOwner . delete ( i ) ; break ; } } let holderTokens = this . _getHolderTokens ( to ) ; holderTokens . push ( tid ) ; this . storage . _tokenOwners . set ( tid , to ) ; ( new Transfer ( from , to , tokenId ) ) ; } dev protected _setTokenURI ( tokenId , _tokenURI ) { assert ( this . _exists ( tokenId ) , "ERC721Metadata: URI set of nonexistent token" ) ; this . storage . _tokenURIs . set ( new UInt128 ( tokenId ) , new ScaleString ( _tokenURI ) ) ; } dev protected _setBaseURI ( baseURI_ ) { this . storage . _baseURI = baseURI_ ; } protected _approve ( to , tokenId ) { this . storage . _tokenApprovals . set ( new UInt128 ( tokenId ) , to ) ; ( new Approval ( this . ownerOf ( tokenId ) , to , tokenId ) ) ; } }