git.delta.rocks / unique-network / refs/commits / abc6b050b3ad

difftreelog

chore implement transfer and burn for ERC-721

Grigoriy Simonov2022-07-28parent: #7d1859c.patch.diff
in: master

9 files changed

modifiedMakefilediffbeforeafterboth
38 PACKAGE=pallet-nonfungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh38 PACKAGE=pallet-nonfungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
39 PACKAGE=pallet-nonfungible NAME=erc::gen_impl OUTPUT=$(NONFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh39 PACKAGE=pallet-nonfungible NAME=erc::gen_impl OUTPUT=$(NONFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
4040
41UniqueRefungible.sol:
42 PACKAGE=pallet-refungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
43 PACKAGE=pallet-refungible NAME=erc::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
44
45UniqueRefungible.sol:41UniqueRefungible.sol:
46 PACKAGE=pallet-refungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh42 PACKAGE=pallet-refungible NAME=erc::gen_iface OUTPUT=$(TESTS_API)/$@ ./.maintain/scripts/generate_sol.sh
47 PACKAGE=pallet-refungible NAME=erc::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh43 PACKAGE=pallet-refungible NAME=erc::gen_impl OUTPUT=$(REFUNGIBLE_EVM_STUBS)/$@ ./.maintain/scripts/generate_sol.sh
74 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungibleToken.raw ./.maintain/scripts/compile_stub.sh70 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungibleToken.raw ./.maintain/scripts/compile_stub.sh
75 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_TOKEN_EVM_ABI) ./.maintain/scripts/generate_abi.sh71 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_TOKEN_EVM_ABI) ./.maintain/scripts/generate_abi.sh
76
77UniqueRefungible: UniqueRefungible.sol
78 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_STUBS)/UniqueRefungible.raw ./.maintain/scripts/compile_stub.sh
79 INPUT=$(REFUNGIBLE_EVM_STUBS)/$< OUTPUT=$(REFUNGIBLE_EVM_ABI) ./.maintain/scripts/generate_abi.sh
8072
81ContractHelpers: ContractHelpers.sol73ContractHelpers: ContractHelpers.sol
82 INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw ./.maintain/scripts/compile_stub.sh74 INPUT=$(CONTRACT_HELPERS_STUBS)/$< OUTPUT=$(CONTRACT_HELPERS_STUBS)/ContractHelpers.raw ./.maintain/scripts/compile_stub.sh
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
34 CommonEvmHandler, CollectionCall,34 CommonEvmHandler, CollectionCall,
35 static_property::{key, value as property_value},35 static_property::{key, value as property_value},
36 },36 },
37 eth::collection_id_to_address,
37};38};
38use pallet_evm::{account::CrossAccountId, PrecompileHandle};39use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm, PrecompileHandle};
39use pallet_evm_coder_substrate::{call, dispatch_to_evm};40use pallet_evm_coder_substrate::{call, dispatch_to_evm};
40use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};41use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
41use sp_core::H160;42use sp_core::H160;
46};47};
4748
48use crate::{49use crate::{
49 AccountBalance, Config, CreateItemData, Pallet, RefungibleHandle, SelfWeightOf,50 AccountBalance, Balance, Config, CreateItemData, Pallet, RefungibleHandle, SelfWeightOf,
50 TokenProperties, TokensMinted, weights::WeightInfo,51 TokenProperties, TokensMinted, TotalSupply, weights::WeightInfo,
51};52};
5253
53/// @title A contract that allows to set and delete token properties and change token property permissions.54/// @title A contract that allows to set and delete token properties and change token property permissions.
331 Err("not implemented".into())332 Err("not implemented".into())
332 }333 }
333334
334 /// @dev Not implemented335 /// @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
336 /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
337 /// THEY MAY BE PERMANENTLY LOST
338 /// @dev Throws unless `msg.sender` is the current owner or an authorized
339 /// operator for this RFT. Throws if `from` is not the current owner. Throws
340 /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
341 /// Throws if RFT pieces have multiple owners.
342 /// @param from The current owner of the NFT
343 /// @param to The new owner
344 /// @param tokenId The NFT to transfer
345 /// @param _value Not used for an NFT
346 #[weight(<SelfWeightOf<T>>::transfer_from_creating_removing())]
335 fn transfer_from(347 fn transfer_from(
336 &mut self,348 &mut self,
337 _caller: caller,349 caller: caller,
338 _from: address,350 from: address,
339 _to: address,351 to: address,
340 _token_id: uint256,352 token_id: uint256,
341 _value: value,353 _value: value,
342 ) -> Result<void> {354 ) -> Result<void> {
355 let caller = T::CrossAccountId::from_eth(caller);
356 let from = T::CrossAccountId::from_eth(from);
357 let to = T::CrossAccountId::from_eth(to);
358 let token = token_id.try_into()?;
359 let budget = self
360 .recorder
361 .weight_calls_budget(<StructureWeight<T>>::find_parent());
362
363 let balance = balance(&self, token, &from)?;
364 ensure_single_owner(&self, token, balance)?;
365
366 <Pallet<T>>::transfer_from(self, &caller, &from, &to, token, balance, &budget)
367 .map_err(dispatch_to_evm::<T>)?;
368
343 Err("not implemented".into())369 <PalletEvm<T>>::deposit_log(
370 ERC721Events::Transfer {
371 from: *from.as_eth(),
372 to: *to.as_eth(),
373 token_id: token_id.into(),
374 }
375 .to_log(collection_id_to_address(self.id)),
376 );
377 Ok(())
344 }378 }
345379
346 /// @dev Not implemented380 /// @dev Not implemented
378 }412 }
379}413}
414
415/// Returns amount of pieces of `token` that `owner` have
416fn balance<T: Config>(
417 collection: &RefungibleHandle<T>,
418 token: TokenId,
419 owner: &T::CrossAccountId,
420) -> Result<u128> {
421 collection.consume_store_reads(1)?;
422 let balance = <Balance<T>>::get((collection.id, token, &owner));
423 Ok(balance)
424}
425
426/// Throws if `owner_balance` is lower than total amount of `token` pieces
427fn ensure_single_owner<T: Config>(
428 collection: &RefungibleHandle<T>,
429 token: TokenId,
430 owner_balance: u128,
431) -> Result<()> {
432 collection.consume_store_reads(1)?;
433 let total_supply = <TotalSupply<T>>::get((collection.id, token));
434 if total_supply != owner_balance {
435 return Err("token has multiple owners".into());
436 }
437 Ok(())
438}
380439
381/// @title ERC721 Token that can be irreversibly burned (destroyed).440/// @title ERC721 Token that can be irreversibly burned (destroyed).
382#[solidity_interface(name = "ERC721Burnable")]441#[solidity_interface(name = "ERC721Burnable")]
383impl<T: Config> RefungibleHandle<T> {442impl<T: Config> RefungibleHandle<T> {
384 /// @dev Not implemented443 /// @notice Burns a specific ERC721 token.
444 /// @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
445 /// operator of the current owner.
446 /// @param tokenId The RFT to approve
447 #[weight(<SelfWeightOf<T>>::burn_item_fully())]
385 fn burn(&mut self, _caller: caller, _token_id: uint256, _value: value) -> Result<void> {448 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
449 let caller = T::CrossAccountId::from_eth(caller);
386 Err("not implemented".into())450 let token = token_id.try_into()?;
451
452 let balance = balance(&self, token, &caller)?;
453 ensure_single_owner(&self, token, balance)?;
454
455 <Pallet<T>>::burn(self, &caller, token, balance).map_err(dispatch_to_evm::<T>)?;
456 Ok(())
387 }457 }
388}458}
389459
555/// @title Unique extensions for ERC721.625/// @title Unique extensions for ERC721.
556#[solidity_interface(name = "ERC721UniqueExtensions")]626#[solidity_interface(name = "ERC721UniqueExtensions")]
557impl<T: Config> RefungibleHandle<T> {627impl<T: Config> RefungibleHandle<T> {
628 /// @notice Transfer ownership of an RFT
629 /// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
630 /// is the zero address. Throws if `tokenId` is not a valid RFT.
631 /// Throws if RFT pieces have multiple owners.
632 /// @param to The new owner
633 /// @param tokenId The RFT to transfer
634 /// @param _value Not used for an RFT
635 #[weight(<SelfWeightOf<T>>::transfer_creating_removing())]
636 fn transfer(
637 &mut self,
638 caller: caller,
639 to: address,
640 token_id: uint256,
641 _value: value,
642 ) -> Result<void> {
643 let caller = T::CrossAccountId::from_eth(caller);
644 let to = T::CrossAccountId::from_eth(to);
645 let token = token_id.try_into()?;
646 let budget = self
647 .recorder
648 .weight_calls_budget(<StructureWeight<T>>::find_parent());
649
650 let balance = balance(&self, token, &caller)?;
651 ensure_single_owner(&self, token, balance)?;
652
653 <Pallet<T>>::transfer(self, &caller, &to, token, balance, &budget)
654 .map_err(dispatch_to_evm::<T>)?;
655 <PalletEvm<T>>::deposit_log(
656 ERC721Events::Transfer {
657 from: *caller.as_eth(),
658 to: *to.as_eth(),
659 token_id: token_id.into(),
660 }
661 .to_log(collection_id_to_address(self.id)),
662 );
663 Ok(())
664 }
665
666 /// @notice Burns a specific ERC721 token.
667 /// @dev Throws unless `msg.sender` is the current owner or an authorized
668 /// operator for this RFT. Throws if `from` is not the current owner. Throws
669 /// if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
670 /// Throws if RFT pieces have multiple owners.
671 /// @param from The current owner of the RFT
672 /// @param tokenId The RFT to transfer
673 /// @param _value Not used for an RFT
674 #[weight(<SelfWeightOf<T>>::burn_from())]
675 fn burn_from(
676 &mut self,
677 caller: caller,
678 from: address,
679 token_id: uint256,
680 _value: value,
681 ) -> Result<void> {
682 let caller = T::CrossAccountId::from_eth(caller);
683 let from = T::CrossAccountId::from_eth(from);
684 let token = token_id.try_into()?;
685 let budget = self
686 .recorder
687 .weight_calls_budget(<StructureWeight<T>>::find_parent());
688
689 let balance = balance(&self, token, &caller)?;
690 ensure_single_owner(&self, token, balance)?;
691
692 <Pallet<T>>::burn_from(self, &caller, &from, token, balance, &budget)
693 .map_err(dispatch_to_evm::<T>)?;
694 Ok(())
695 }
696
558 /// @notice Returns next free RFT ID.697 /// @notice Returns next free RFT ID.
559 fn next_token_id(&self) -> Result<uint256> {698 fn next_token_id(&self) -> Result<uint256> {
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
454 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);454 <PalletStructure<T>>::unnest_if_nested(owner, collection.id, token);
455 <AccountBalance<T>>::insert((collection.id, owner), account_balance);455 <AccountBalance<T>>::insert((collection.id, owner), account_balance);
456 Self::burn_token_unchecked(collection, token)?;456 Self::burn_token_unchecked(collection, token)?;
457 <PalletEvm<T>>::deposit_log(
458 ERC721Events::Transfer {
459 from: *owner.as_eth(),
460 to: H160::default(),
461 token_id: token.into(),
462 }
463 .to_log(collection_id_to_address(collection.id)),
464 );
457 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(465 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(
458 collection.id,466 collection.id,
459 token,467 token,
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
51 event MintingFinished();51 event MintingFinished();
52}52}
53
54// Selector: 0784ee64
55contract ERC721UniqueExtensions is Dummy, ERC165 {
56 // @notice Returns next free RFT ID.
57 //
58 // Selector: nextTokenId() 75794a3c
59 function nextTokenId() public view returns (uint256) {
60 require(false, stub_error);
61 dummy;
62 return 0;
63 }
64
65 // Selector: mintBulk(address,uint256[]) 44a9945e
66 function mintBulk(address to, uint256[] memory tokenIds)
67 public
68 returns (bool)
69 {
70 require(false, stub_error);
71 to;
72 tokenIds;
73 dummy = 0;
74 return false;
75 }
76
77 // Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
78 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
79 public
80 returns (bool)
81 {
82 require(false, stub_error);
83 to;
84 tokens;
85 dummy = 0;
86 return false;
87 }
88}
8953
90// Selector: 4136937754// Selector: 41369377
91contract TokenProperties is Dummy, ERC165 {55contract TokenProperties is Dummy, ERC165 {
56 // @notice Set permissions for token property.
57 // @dev Throws error if `msg.sender` is not admin or owner of the collection.
58 // @param key Property key.
59 // @param is_mutable Permission to mutate property.
60 // @param collection_admin Permission to mutate property by collection admin if property is mutable.
61 // @param token_owner Permission to mutate property by token owner if property is mutable.
62 //
92 // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa63 // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
93 function setTokenPropertyPermission(64 function setTokenPropertyPermission(
94 string memory key,65 string memory key,
104 dummy = 0;75 dummy = 0;
105 }76 }
10677
78 // @notice Set token property value.
79 // @dev Throws error if `msg.sender` has no permission to edit the property.
80 // @param tokenId ID of the token.
81 // @param key Property key.
82 // @param value Property value.
83 //
107 // Selector: setProperty(uint256,string,bytes) 1752d67b84 // Selector: setProperty(uint256,string,bytes) 1752d67b
108 function setProperty(85 function setProperty(
109 uint256 tokenId,86 uint256 tokenId,
117 dummy = 0;94 dummy = 0;
118 }95 }
11996
97 // @notice Delete token property value.
98 // @dev Throws error if `msg.sender` has no permission to edit the property.
99 // @param tokenId ID of the token.
100 // @param key Property key.
101 //
120 // Selector: deleteProperty(uint256,string) 066111d1102 // Selector: deleteProperty(uint256,string) 066111d1
121 function deleteProperty(uint256 tokenId, string memory key) public {103 function deleteProperty(uint256 tokenId, string memory key) public {
122 require(false, stub_error);104 require(false, stub_error);
125 dummy = 0;107 dummy = 0;
126 }108 }
127109
110 // @notice Get token property value.
128 // Throws error if key not found111 // @dev Throws error if key not found
112 // @param tokenId ID of the token.
113 // @param key Property key.
114 // @return Property value bytes
129 //115 //
130 // Selector: property(uint256,string) 7228c327116 // Selector: property(uint256,string) 7228c327
131 function property(uint256 tokenId, string memory key)117 function property(uint256 tokenId, string memory key)
143129
144// Selector: 42966c68130// Selector: 42966c68
145contract ERC721Burnable is Dummy, ERC165 {131contract ERC721Burnable is Dummy, ERC165 {
146 // @dev Not implemented132 // @notice Burns a specific ERC721 token.
133 // @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
134 // operator of the current owner.
135 // @param tokenId The RFT to approve
147 //136 //
148 // Selector: burn(uint256) 42966c68137 // Selector: burn(uint256) 42966c68
149 function burn(uint256 tokenId) public {138 function burn(uint256 tokenId) public {
155144
156// Selector: 58800161145// Selector: 58800161
157contract ERC721 is Dummy, ERC165, ERC721Events {146contract ERC721 is Dummy, ERC165, ERC721Events {
147 // @notice Count all RFTs assigned to an owner
148 // @dev RFTs assigned to the zero address are considered invalid, and this
149 // function throws for queries about the zero address.
150 // @param owner An address for whom to query the balance
151 // @return The number of RFTs owned by `owner`, possibly zero
152 //
158 // Selector: balanceOf(address) 70a08231153 // Selector: balanceOf(address) 70a08231
159 function balanceOf(address owner) public view returns (uint256) {154 function balanceOf(address owner) public view returns (uint256) {
160 require(false, stub_error);155 require(false, stub_error);
203 dummy = 0;198 dummy = 0;
204 }199 }
205200
206 // @dev Not implemented201 // @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
202 // TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
203 // THEY MAY BE PERMANENTLY LOST
204 // @dev Throws unless `msg.sender` is the current owner or an authorized
205 // operator for this RFT. Throws if `from` is not the current owner. Throws
206 // if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
207 // Throws if RFT pieces have multiple owners.
208 // @param from The current owner of the NFT
209 // @param to The new owner
210 // @param tokenId The NFT to transfer
211 // @param _value Not used for an NFT
207 //212 //
208 // Selector: transferFrom(address,address,uint256) 23b872dd213 // Selector: transferFrom(address,address,uint256) 23b872dd
209 function transferFrom(214 function transferFrom(
266271
267// Selector: 5b5e139f272// Selector: 5b5e139f
268contract ERC721Metadata is Dummy, ERC165 {273contract ERC721Metadata is Dummy, ERC165 {
274 // @notice A descriptive name for a collection of RFTs in this contract
275 //
269 // Selector: name() 06fdde03276 // Selector: name() 06fdde03
270 function name() public view returns (string memory) {277 function name() public view returns (string memory) {
271 require(false, stub_error);278 require(false, stub_error);
272 dummy;279 dummy;
273 return "";280 return "";
274 }281 }
275282
283 // @notice An abbreviated name for RFTs in this contract
284 //
276 // Selector: symbol() 95d89b41285 // Selector: symbol() 95d89b41
277 function symbol() public view returns (string memory) {286 function symbol() public view returns (string memory) {
278 require(false, stub_error);287 require(false, stub_error);
279 dummy;288 dummy;
280 return "";289 return "";
281 }290 }
282291
292 // @notice A distinct Uniform Resource Identifier (URI) for a given asset.
293 //
294 // @dev If the token has a `url` property and it is not empty, it is returned.
295 // Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
296 // If the collection property `baseURI` is empty or absent, return "" (empty string)
297 // otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
298 // otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
299 //
283 // Returns token's const_metadata300 // @return token's const_metadata
284 //301 //
285 // Selector: tokenURI(uint256) c87b56dd302 // Selector: tokenURI(uint256) c87b56dd
286 function tokenURI(uint256 tokenId) public view returns (string memory) {303 function tokenURI(uint256 tokenId) public view returns (string memory) {
300 return false;317 return false;
301 }318 }
302319
320 // @notice Function to mint token.
303 // `token_id` should be obtained with `next_token_id` method,321 // @dev `tokenId` should be obtained with `nextTokenId` method,
304 // unlike standard, you can't specify it manually322 // unlike standard, you can't specify it manually
323 // @param to The new owner
324 // @param tokenId ID of the minted RFT
305 //325 //
306 // Selector: mint(address,uint256) 40c10f19326 // Selector: mint(address,uint256) 40c10f19
307 function mint(address to, uint256 tokenId) public returns (bool) {327 function mint(address to, uint256 tokenId) public returns (bool) {
312 return false;332 return false;
313 }333 }
314334
335 // @notice Function to mint token with the given tokenUri.
315 // `token_id` should be obtained with `next_token_id` method,336 // @dev `tokenId` should be obtained with `nextTokenId` method,
316 // unlike standard, you can't specify it manually337 // unlike standard, you can't specify it manually
338 // @param to The new owner
339 // @param tokenId ID of the minted RFT
340 // @param tokenUri Token URI that would be stored in the RFT properties
317 //341 //
318 // Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f342 // Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
319 function mintWithTokenURI(343 function mintWithTokenURI(
341365
342// Selector: 780e9d63366// Selector: 780e9d63
343contract ERC721Enumerable is Dummy, ERC165 {367contract ERC721Enumerable is Dummy, ERC165 {
368 // @notice Enumerate valid RFTs
369 // @param index A counter less than `totalSupply()`
370 // @return The token identifier for the `index`th NFT,
371 // (sort order not specified)
372 //
344 // Selector: tokenByIndex(uint256) 4f6ccce7373 // Selector: tokenByIndex(uint256) 4f6ccce7
345 function tokenByIndex(uint256 index) public view returns (uint256) {374 function tokenByIndex(uint256 index) public view returns (uint256) {
346 require(false, stub_error);375 require(false, stub_error);
364 return 0;393 return 0;
365 }394 }
366395
396 // @notice Count RFTs tracked by this contract
397 // @return A count of valid RFTs tracked by this contract, where each one of
398 // them has an assigned and queryable owner not equal to the zero address
399 //
367 // Selector: totalSupply() 18160ddd400 // Selector: totalSupply() 18160ddd
368 function totalSupply() public view returns (uint256) {401 function totalSupply() public view returns (uint256) {
369 require(false, stub_error);402 require(false, stub_error);
599 }632 }
600}633}
634
635// Selector: d74d154f
636contract ERC721UniqueExtensions is Dummy, ERC165 {
637 // @notice Transfer ownership of an RFT
638 // @dev Throws unless `msg.sender` is the current owner. Throws if `to`
639 // is the zero address. Throws if `tokenId` is not a valid RFT.
640 // Throws if RFT pieces have multiple owners.
641 // @param to The new owner
642 // @param tokenId The RFT to transfer
643 // @param _value Not used for an RFT
644 //
645 // Selector: transfer(address,uint256) a9059cbb
646 function transfer(address to, uint256 tokenId) public {
647 require(false, stub_error);
648 to;
649 tokenId;
650 dummy = 0;
651 }
652
653 // @notice Burns a specific ERC721 token.
654 // @dev Throws unless `msg.sender` is the current owner or an authorized
655 // operator for this RFT. Throws if `from` is not the current owner. Throws
656 // if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
657 // Throws if RFT pieces have multiple owners.
658 // @param from The current owner of the RFT
659 // @param tokenId The RFT to transfer
660 // @param _value Not used for an RFT
661 //
662 // Selector: burnFrom(address,uint256) 79cc6790
663 function burnFrom(address from, uint256 tokenId) public {
664 require(false, stub_error);
665 from;
666 tokenId;
667 dummy = 0;
668 }
669
670 // @notice Returns next free RFT ID.
671 //
672 // Selector: nextTokenId() 75794a3c
673 function nextTokenId() public view returns (uint256) {
674 require(false, stub_error);
675 dummy;
676 return 0;
677 }
678
679 // @notice Function to mint multiple tokens.
680 // @dev `tokenIds` should be an array of consecutive numbers and first number
681 // should be obtained with `nextTokenId` method
682 // @param to The new owner
683 // @param tokenIds IDs of the minted RFTs
684 //
685 // Selector: mintBulk(address,uint256[]) 44a9945e
686 function mintBulk(address to, uint256[] memory tokenIds)
687 public
688 returns (bool)
689 {
690 require(false, stub_error);
691 to;
692 tokenIds;
693 dummy = 0;
694 return false;
695 }
696
697 // @notice Function to mint multiple tokens with the given tokenUris.
698 // @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
699 // numbers and first number should be obtained with `nextTokenId` method
700 // @param to The new owner
701 // @param tokens array of pairs of token ID and token URI for minted tokens
702 //
703 // Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
704 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
705 public
706 returns (bool)
707 {
708 require(false, stub_error);
709 to;
710 tokens;
711 dummy = 0;
712 return false;
713 }
714}
601715
602contract UniqueRefungible is716contract UniqueRefungible is
603 Dummy,717 Dummy,
modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterboth

binary blob — no preview

modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
42 event MintingFinished();42 event MintingFinished();
43}43}
44
45// Selector: 0784ee64
46interface ERC721UniqueExtensions is Dummy, ERC165 {
47 // @notice Returns next free RFT ID.
48 //
49 // Selector: nextTokenId() 75794a3c
50 function nextTokenId() external view returns (uint256);
51
52 // Selector: mintBulk(address,uint256[]) 44a9945e
53 function mintBulk(address to, uint256[] memory tokenIds)
54 external
55 returns (bool);
56
57 // Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
58 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
59 external
60 returns (bool);
61}
6244
63// Selector: 4136937745// Selector: 41369377
64interface TokenProperties is Dummy, ERC165 {46interface TokenProperties is Dummy, ERC165 {
47 // @notice Set permissions for token property.
48 // @dev Throws error if `msg.sender` is not admin or owner of the collection.
49 // @param key Property key.
50 // @param is_mutable Permission to mutate property.
51 // @param collection_admin Permission to mutate property by collection admin if property is mutable.
52 // @param token_owner Permission to mutate property by token owner if property is mutable.
53 //
65 // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa54 // Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
66 function setTokenPropertyPermission(55 function setTokenPropertyPermission(
67 string memory key,56 string memory key,
70 bool tokenOwner59 bool tokenOwner
71 ) external;60 ) external;
7261
62 // @notice Set token property value.
63 // @dev Throws error if `msg.sender` has no permission to edit the property.
64 // @param tokenId ID of the token.
65 // @param key Property key.
66 // @param value Property value.
67 //
73 // Selector: setProperty(uint256,string,bytes) 1752d67b68 // Selector: setProperty(uint256,string,bytes) 1752d67b
74 function setProperty(69 function setProperty(
75 uint256 tokenId,70 uint256 tokenId,
76 string memory key,71 string memory key,
77 bytes memory value72 bytes memory value
78 ) external;73 ) external;
7974
75 // @notice Delete token property value.
76 // @dev Throws error if `msg.sender` has no permission to edit the property.
77 // @param tokenId ID of the token.
78 // @param key Property key.
79 //
80 // Selector: deleteProperty(uint256,string) 066111d180 // Selector: deleteProperty(uint256,string) 066111d1
81 function deleteProperty(uint256 tokenId, string memory key) external;81 function deleteProperty(uint256 tokenId, string memory key) external;
8282
83 // @notice Get token property value.
83 // Throws error if key not found84 // @dev Throws error if key not found
85 // @param tokenId ID of the token.
86 // @param key Property key.
87 // @return Property value bytes
84 //88 //
85 // Selector: property(uint256,string) 7228c32789 // Selector: property(uint256,string) 7228c327
86 function property(uint256 tokenId, string memory key)90 function property(uint256 tokenId, string memory key)
9195
92// Selector: 42966c6896// Selector: 42966c68
93interface ERC721Burnable is Dummy, ERC165 {97interface ERC721Burnable is Dummy, ERC165 {
94 // @dev Not implemented98 // @notice Burns a specific ERC721 token.
99 // @dev Throws unless `msg.sender` is the current RFT owner, or an authorized
100 // operator of the current owner.
101 // @param tokenId The RFT to approve
95 //102 //
96 // Selector: burn(uint256) 42966c68103 // Selector: burn(uint256) 42966c68
97 function burn(uint256 tokenId) external;104 function burn(uint256 tokenId) external;
98}105}
99106
100// Selector: 58800161107// Selector: 58800161
101interface ERC721 is Dummy, ERC165, ERC721Events {108interface ERC721 is Dummy, ERC165, ERC721Events {
109 // @notice Count all RFTs assigned to an owner
110 // @dev RFTs assigned to the zero address are considered invalid, and this
111 // function throws for queries about the zero address.
112 // @param owner An address for whom to query the balance
113 // @return The number of RFTs owned by `owner`, possibly zero
114 //
102 // Selector: balanceOf(address) 70a08231115 // Selector: balanceOf(address) 70a08231
103 function balanceOf(address owner) external view returns (uint256);116 function balanceOf(address owner) external view returns (uint256);
104117
124 uint256 tokenId137 uint256 tokenId
125 ) external;138 ) external;
126139
127 // @dev Not implemented140 // @notice Transfer ownership of an RFT -- THE CALLER IS RESPONSIBLE
141 // TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE
142 // THEY MAY BE PERMANENTLY LOST
143 // @dev Throws unless `msg.sender` is the current owner or an authorized
144 // operator for this RFT. Throws if `from` is not the current owner. Throws
145 // if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
146 // Throws if RFT pieces have multiple owners.
147 // @param from The current owner of the NFT
148 // @param to The new owner
149 // @param tokenId The NFT to transfer
150 // @param _value Not used for an NFT
128 //151 //
129 // Selector: transferFrom(address,address,uint256) 23b872dd152 // Selector: transferFrom(address,address,uint256) 23b872dd
130 function transferFrom(153 function transferFrom(
159182
160// Selector: 5b5e139f183// Selector: 5b5e139f
161interface ERC721Metadata is Dummy, ERC165 {184interface ERC721Metadata is Dummy, ERC165 {
185 // @notice A descriptive name for a collection of RFTs in this contract
186 //
162 // Selector: name() 06fdde03187 // Selector: name() 06fdde03
163 function name() external view returns (string memory);188 function name() external view returns (string memory);
164189
190 // @notice An abbreviated name for RFTs in this contract
191 //
165 // Selector: symbol() 95d89b41192 // Selector: symbol() 95d89b41
166 function symbol() external view returns (string memory);193 function symbol() external view returns (string memory);
167194
195 // @notice A distinct Uniform Resource Identifier (URI) for a given asset.
196 //
197 // @dev If the token has a `url` property and it is not empty, it is returned.
198 // Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
199 // If the collection property `baseURI` is empty or absent, return "" (empty string)
200 // otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
201 // otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
202 //
168 // Returns token's const_metadata203 // @return token's const_metadata
169 //204 //
170 // Selector: tokenURI(uint256) c87b56dd205 // Selector: tokenURI(uint256) c87b56dd
171 function tokenURI(uint256 tokenId) external view returns (string memory);206 function tokenURI(uint256 tokenId) external view returns (string memory);
176 // Selector: mintingFinished() 05d2035b211 // Selector: mintingFinished() 05d2035b
177 function mintingFinished() external view returns (bool);212 function mintingFinished() external view returns (bool);
178213
214 // @notice Function to mint token.
179 // `token_id` should be obtained with `next_token_id` method,215 // @dev `tokenId` should be obtained with `nextTokenId` method,
180 // unlike standard, you can't specify it manually216 // unlike standard, you can't specify it manually
217 // @param to The new owner
218 // @param tokenId ID of the minted RFT
181 //219 //
182 // Selector: mint(address,uint256) 40c10f19220 // Selector: mint(address,uint256) 40c10f19
183 function mint(address to, uint256 tokenId) external returns (bool);221 function mint(address to, uint256 tokenId) external returns (bool);
184222
223 // @notice Function to mint token with the given tokenUri.
185 // `token_id` should be obtained with `next_token_id` method,224 // @dev `tokenId` should be obtained with `nextTokenId` method,
186 // unlike standard, you can't specify it manually225 // unlike standard, you can't specify it manually
226 // @param to The new owner
227 // @param tokenId ID of the minted RFT
228 // @param tokenUri Token URI that would be stored in the RFT properties
187 //229 //
188 // Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f230 // Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
189 function mintWithTokenURI(231 function mintWithTokenURI(
200242
201// Selector: 780e9d63243// Selector: 780e9d63
202interface ERC721Enumerable is Dummy, ERC165 {244interface ERC721Enumerable is Dummy, ERC165 {
245 // @notice Enumerate valid RFTs
246 // @param index A counter less than `totalSupply()`
247 // @return The token identifier for the `index`th NFT,
248 // (sort order not specified)
249 //
203 // Selector: tokenByIndex(uint256) 4f6ccce7250 // Selector: tokenByIndex(uint256) 4f6ccce7
204 function tokenByIndex(uint256 index) external view returns (uint256);251 function tokenByIndex(uint256 index) external view returns (uint256);
205252
211 view258 view
212 returns (uint256);259 returns (uint256);
213260
261 // @notice Count RFTs tracked by this contract
262 // @return A count of valid RFTs tracked by this contract, where each one of
263 // them has an assigned and queryable owner not equal to the zero address
264 //
214 // Selector: totalSupply() 18160ddd265 // Selector: totalSupply() 18160ddd
215 function totalSupply() external view returns (uint256);266 function totalSupply() external view returns (uint256);
216}267}
363 function setCollectionMintMode(bool mode) external;414 function setCollectionMintMode(bool mode) external;
364}415}
416
417// Selector: d74d154f
418interface ERC721UniqueExtensions is Dummy, ERC165 {
419 // @notice Transfer ownership of an RFT
420 // @dev Throws unless `msg.sender` is the current owner. Throws if `to`
421 // is the zero address. Throws if `tokenId` is not a valid RFT.
422 // Throws if RFT pieces have multiple owners.
423 // @param to The new owner
424 // @param tokenId The RFT to transfer
425 // @param _value Not used for an RFT
426 //
427 // Selector: transfer(address,uint256) a9059cbb
428 function transfer(address to, uint256 tokenId) external;
429
430 // @notice Burns a specific ERC721 token.
431 // @dev Throws unless `msg.sender` is the current owner or an authorized
432 // operator for this RFT. Throws if `from` is not the current owner. Throws
433 // if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
434 // Throws if RFT pieces have multiple owners.
435 // @param from The current owner of the RFT
436 // @param tokenId The RFT to transfer
437 // @param _value Not used for an RFT
438 //
439 // Selector: burnFrom(address,uint256) 79cc6790
440 function burnFrom(address from, uint256 tokenId) external;
441
442 // @notice Returns next free RFT ID.
443 //
444 // Selector: nextTokenId() 75794a3c
445 function nextTokenId() external view returns (uint256);
446
447 // @notice Function to mint multiple tokens.
448 // @dev `tokenIds` should be an array of consecutive numbers and first number
449 // should be obtained with `nextTokenId` method
450 // @param to The new owner
451 // @param tokenIds IDs of the minted RFTs
452 //
453 // Selector: mintBulk(address,uint256[]) 44a9945e
454 function mintBulk(address to, uint256[] memory tokenIds)
455 external
456 returns (bool);
457
458 // @notice Function to mint multiple tokens with the given tokenUris.
459 // @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
460 // numbers and first number should be obtained with `nextTokenId` method
461 // @param to The new owner
462 // @param tokens array of pairs of token ID and token URI for minted tokens
463 //
464 // Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
465 function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
466 external
467 returns (bool);
468}
365469
366interface UniqueRefungible is470interface UniqueRefungible is
367 Dummy,471 Dummy,
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {createCollectionExpectSuccess} from '../util/helpers';17import {createCollectionExpectSuccess, UNIQUE} from '../util/helpers';
18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, tokenIdToAddress} from './util/helpers';18import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, tokenIdToAddress} from './util/helpers';
19import reFungibleAbi from './reFungibleAbi.json';19import reFungibleAbi from './reFungibleAbi.json';
20import reFungibleTokenAbi from './reFungibleTokenAbi.json';20import reFungibleTokenAbi from './reFungibleTokenAbi.json';
21import {expect} from 'chai';21import {expect} from 'chai';
26 const helper = evmCollectionHelpers(web3, caller);26 const helper = evmCollectionHelpers(web3, caller);
27 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();27 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
28 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);28 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
29 const contract = new web3.eth.Contract(reFungibleAbi as any, collectionIdAddress, {from: caller, ...GAS_ARGS});29 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
30 const nextTokenId = await contract.methods.nextTokenId().call();30 const nextTokenId = await contract.methods.nextTokenId().call();
31 await contract.methods.mint(caller, nextTokenId).send();31 await contract.methods.mint(caller, nextTokenId).send();
32 const totalSupply = await contract.methods.totalSupply().call();32 const totalSupply = await contract.methods.totalSupply().call();
38 const helper = evmCollectionHelpers(web3, caller);38 const helper = evmCollectionHelpers(web3, caller);
39 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();39 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
40 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);40 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
41 const contract = new web3.eth.Contract(reFungibleAbi as any, collectionIdAddress, {from: caller, ...GAS_ARGS});41 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
4242
43 {43 {
44 const nextTokenId = await contract.methods.nextTokenId().call();44 const nextTokenId = await contract.methods.nextTokenId().call();
63 const helper = evmCollectionHelpers(web3, caller);63 const helper = evmCollectionHelpers(web3, caller);
64 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();64 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
65 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);65 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
66 const contract = new web3.eth.Contract(reFungibleAbi as any, collectionIdAddress, {from: caller, ...GAS_ARGS});66 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
6767
68 const tokenId = await contract.methods.nextTokenId().call();68 const tokenId = await contract.methods.nextTokenId().call();
69 await contract.methods.mint(caller, tokenId).send();69 await contract.methods.mint(caller, tokenId).send();
79 const helper = evmCollectionHelpers(web3, caller);79 const helper = evmCollectionHelpers(web3, caller);
80 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();80 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
81 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);81 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
82 const contract = new web3.eth.Contract(reFungibleAbi as any, collectionIdAddress, {from: caller, ...GAS_ARGS});82 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
8383
84 const tokenId = await contract.methods.nextTokenId().call();84 const tokenId = await contract.methods.nextTokenId().call();
85 await contract.methods.mint(caller, tokenId).send();85 await contract.methods.mint(caller, tokenId).send();
105 let result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();105 let result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
106 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);106 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
107 const receiver = createEthAccount(web3);107 const receiver = createEthAccount(web3);
108 const contract = evmCollection(web3, owner, collectionIdAddress);108 const contract = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
109 const nextTokenId = await contract.methods.nextTokenId().call();109 const nextTokenId = await contract.methods.nextTokenId().call();
110110
111 expect(nextTokenId).to.be.equal('1');111 expect(nextTokenId).to.be.equal('1');
137 const helper = evmCollectionHelpers(web3, caller);137 const helper = evmCollectionHelpers(web3, caller);
138 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();138 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
139 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);139 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
140 const contract = new web3.eth.Contract(reFungibleAbi as any, collectionIdAddress, {from: caller, ...GAS_ARGS});140 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
141141
142 const receiver = createEthAccount(web3);142 const receiver = createEthAccount(web3);
143143
190 }190 }
191 });191 });
192
193 itWeb3('Can perform burn()', async ({web3, api, privateKeyWrapper}) => {
194 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
195 const helper = evmCollectionHelpers(web3, caller);
196 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
197 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
198 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
199
200 const tokenId = await contract.methods.nextTokenId().call();
201 await contract.methods.mint(caller, tokenId).send();
202 {
203 const result = await contract.methods.burn(tokenId).send();
204 const events = normalizeEvents(result.events);
205
206 expect(events).to.be.deep.equal([
207 {
208 address: collectionIdAddress,
209 event: 'Transfer',
210 args: {
211 from: caller,
212 to: '0x0000000000000000000000000000000000000000',
213 tokenId: tokenId.toString(),
214 },
215 },
216 ]);
217 }
218 });
219
220 itWeb3('Can perform transferFrom()', async ({web3, api, privateKeyWrapper}) => {
221 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
222 const helper = evmCollectionHelpers(web3, caller);
223 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
224 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
225 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
226
227 const receiver = createEthAccount(web3);
228
229 const tokenId = await contract.methods.nextTokenId().call();
230 await contract.methods.mint(caller, tokenId).send();
231 {
232 const result = await contract.methods.transferFrom(caller, receiver, tokenId).send();
233 const events = normalizeEvents(result.events);
234 expect(events).to.include.deep.members([
235 {
236 address: collectionIdAddress,
237 event: 'Transfer',
238 args: {
239 from: caller,
240 to: receiver,
241 tokenId: tokenId.toString(),
242 },
243 },
244 ]);
245 }
246
247 {
248 const balance = await contract.methods.balanceOf(receiver).call();
249 expect(+balance).to.equal(1);
250 }
251
252 {
253 const balance = await contract.methods.balanceOf(caller).call();
254 expect(+balance).to.equal(0);
255 }
256 });
257
258 itWeb3('Can perform transfer()', async ({web3, api, privateKeyWrapper}) => {
259 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
260 const helper = evmCollectionHelpers(web3, caller);
261 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
262 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
263 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
264
265 const receiver = createEthAccount(web3);
266
267 const tokenId = await contract.methods.nextTokenId().call();
268 await contract.methods.mint(caller, tokenId).send();
269
270 {
271 const result = await contract.methods.transfer(receiver, tokenId).send();
272 const events = normalizeEvents(result.events);
273 expect(events).to.include.deep.members([
274 {
275 address: collectionIdAddress,
276 event: 'Transfer',
277 args: {
278 from: caller,
279 to: receiver,
280 tokenId: tokenId.toString(),
281 },
282 },
283 ]);
284 }
285
286 {
287 const balance = await contract.methods.balanceOf(caller).call();
288 expect(+balance).to.equal(0);
289 }
290
291 {
292 const balance = await contract.methods.balanceOf(receiver).call();
293 expect(+balance).to.equal(1);
294 }
295 });
296});
297
298describe('RFT: Fees', () => {
299 itWeb3('transferFrom() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
300 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
301 const helper = evmCollectionHelpers(web3, caller);
302 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
303 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
304 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
305
306 const receiver = createEthAccount(web3);
307
308 const tokenId = await contract.methods.nextTokenId().call();
309 await contract.methods.mint(caller, tokenId).send();
310
311 const cost = await recordEthFee(api, caller, () => contract.methods.transferFrom(caller, receiver, tokenId).send());
312 expect(cost < BigInt(0.2 * Number(UNIQUE)));
313 expect(cost > 0n);
314 });
315
316 itWeb3('transfer() call fee is less than 0.2UNQ', async ({web3, api, privateKeyWrapper}) => {
317 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
318 const helper = evmCollectionHelpers(web3, caller);
319 const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send();
320 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
321 const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'});
322
323 const receiver = createEthAccount(web3);
324
325 const tokenId = await contract.methods.nextTokenId().call();
326 await contract.methods.mint(caller, tokenId).send();
327
328 const cost = await recordEthFee(api, caller, () => contract.methods.transfer(receiver, tokenId).send());
329 expect(cost < BigInt(0.2 * Number(UNIQUE)));
330 expect(cost > 0n);
331 });
192});332});
193333
194describe('Common metadata', () => {334describe('Common metadata', () => {
200 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);340 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
201341
202 const address = collectionIdToAddress(collection);342 const address = collectionIdToAddress(collection);
203 const contract = new web3.eth.Contract(reFungibleAbi as any, address, {from: caller, ...GAS_ARGS});343 const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
204 const name = await contract.methods.name().call();344 const name = await contract.methods.name().call();
205345
206 expect(name).to.equal('token name');346 expect(name).to.equal('token name');
214 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);354 const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
215355
216 const address = collectionIdToAddress(collection);356 const address = collectionIdToAddress(collection);
217 const contract = new web3.eth.Contract(reFungibleAbi as any, address, {from: caller, ...GAS_ARGS});357 const contract = evmCollection(web3, caller, address, {type: 'ReFungible'});
218 const symbol = await contract.methods.symbol().call();358 const symbol = await contract.methods.symbol().call();
219359
220 expect(symbol).to.equal('TOK');360 expect(symbol).to.equal('TOK');
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
135 "stateMutability": "nonpayable",135 "stateMutability": "nonpayable",
136 "type": "function"136 "type": "function"
137 },137 },
138 {
139 "inputs": [
140 { "internalType": "address", "name": "from", "type": "address" },
141 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
142 ],
143 "name": "burnFrom",
144 "outputs": [],
145 "stateMutability": "nonpayable",
146 "type": "function"
147 },
138 {148 {
139 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],149 "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
140 "name": "collectionProperty",150 "name": "collectionProperty",
496 "stateMutability": "view",506 "stateMutability": "view",
497 "type": "function"507 "type": "function"
498 },508 },
509 {
510 "inputs": [
511 { "internalType": "address", "name": "to", "type": "address" },
512 { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
513 ],
514 "name": "transfer",
515 "outputs": [],
516 "stateMutability": "nonpayable",
517 "type": "function"
518 },
499 {519 {
500 "inputs": [520 "inputs": [
501 { "internalType": "address", "name": "from", "type": "address" },521 { "internalType": "address", "name": "from", "type": "address" },