git.delta.rocks / unique-network / refs/commits / 5fc39c3d8a70

difftreelog

refactor rewrite nft pallet to evm helpers

Yaroslav Bolyukin2021-07-27parent: #b905ada.patch.diff
in: master

7 files changed

modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
3939
40 'primitive-types/std',40 'primitive-types/std',
41 'evm-coder/std',41 'evm-coder/std',
42 'evm-coder-substrate/std',42 'pallet-evm-coder-substrate/std',
43]43]
4444
45################################################################################45################################################################################
144sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" }144sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" }
145145
146evm-coder = { default-features = false, path = "../../crates/evm-coder" }146evm-coder = { default-features = false, path = "../../crates/evm-coder" }
147evm-coder-substrate = { default-features = false, path = "../../crates/evm-coder-substrate" }147pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
148primitive-types = { version = "0.9.0", default-features = false, features = ["serde_no_std"] }148primitive-types = { version = "0.9.0", default-features = false, features = [
149 "serde_no_std",
150] }
modifiedpallets/nft/src/eth/erc.rsdiffbeforeafterboth
1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
1use evm_coder::{solidity_interface, solidity, types::*, ToLog};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};
3use core::convert::TryInto;
4use alloc::format;
5use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};
6use frame_support::storage::StorageDoubleMap;
7use pallet_evm::AddressMapping;
8use super::account::CrossAccountId;
2use sp_std::vec::Vec;9use sp_std::vec::Vec;
310
4#[solidity_interface]11#[solidity_interface(name = "ERC165")]
5pub trait InlineNameSymbol {
6 type Error;
7
8 fn name(&self) -> Result<string, Self::Error>;12impl<T: Config> CollectionHandle<T> {
9 fn symbol(&self) -> Result<string, Self::Error>;13 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {
10}14 Ok(match self.mode {
15 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),
16 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),
17 _ => false,
18 })
19 }
20}
1121
12#[solidity_interface]22#[solidity_interface(name = "InlineNameSymbol")]
13pub trait InlineTotalSupply {23impl<T: Config> CollectionHandle<T> {
14 type Error;24 fn name(&self) -> Result<string> {
25 Ok(decode_utf16(self.name.iter().copied())
26 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
27 .collect::<string>())
28 }
1529
16 fn total_supply(&self) -> Result<uint256, Self::Error>;30 fn symbol(&self) -> Result<string> {
31 Ok(string::from_utf8_lossy(&self.token_prefix).into())
32 }
17}33}
1834
19#[solidity_interface]35#[solidity_interface(name = "InlineTotalSupply")]
20pub trait ERC165 {36impl<T: Config> CollectionHandle<T> {
21 type Error;
22
23 fn supports_interface(&self, interface_id: bytes4) -> Result<bool, Self::Error>;37 fn total_supply(&self) -> Result<uint256> {
24}38 // TODO: we do not track total amount of all tokens
39 Ok(0.into())
40 }
41}
2542
26#[solidity_interface(inline_is(InlineNameSymbol))]43#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
27pub trait ERC721Metadata {44impl<T: Config> CollectionHandle<T> {
28 type Error;
29
30 #[solidity(rename_selector = "tokenURI")]
31 fn token_uri(&self, token_id: uint256) -> Result<string, Self::Error>;45 fn token_uri(&self, token_id: uint256) -> Result<string> {
32}46 // TODO: We should standartize url prefix, maybe via offchain schema?
47 Ok(format!("unique.network/{}/{}", self.id, token_id))
48 }
49}
3350
34#[solidity_interface(inline_is(InlineTotalSupply))]51#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]
35pub trait ERC721Enumerable {52impl<T: Config> CollectionHandle<T> {
36 type Error;
37
38 fn token_by_index(&self, index: uint256) -> Result<uint256, Self::Error>;53 fn token_by_index(&self, index: uint256) -> Result<uint256> {
54 Ok(index)
55 }
56
39 fn token_of_owner_by_index(57 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
40 &self,58 // TODO: Not implemetable
41 owner: address,59 Err("not implemented".into())
42 index: uint256,60 }
43 ) -> Result<uint256, Self::Error>;61}
44}
4562
46#[derive(ToLog)]63#[derive(ToLog)]
47pub enum ERC721Events {64pub enum ERC721Events {
71 },88 },
72}89}
7390
74#[solidity_interface(is(ERC165), events(ERC721Events))]91#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]
75pub trait ERC721 {92impl<T: Config> CollectionHandle<T> {
76 type Error;93 #[solidity(rename_selector = "balanceOf")]
77
78 fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;94 fn balance_of_nft(&self, owner: address) -> Result<uint256> {
95 let owner = T::EvmAddressMapping::into_account_id(owner);
96 let balance = <Balance<T>>::get(self.id, owner);
97 Ok(balance.into())
98 }
79 fn owner_of(&self, token_id: uint256) -> Result<address, Self::Error>;99 fn owner_of(&self, token_id: uint256) -> Result<address> {
80100 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
81 #[solidity(rename_selector = "safeTransferFrom")]101 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;
102 Ok(*token.owner.as_eth())
103 }
82 fn safe_transfer_from_with_data(104 fn safe_transfer_from_with_data(
83 &mut self,105 &mut self,
84 from: address,106 _from: address,
85 to: address,107 _to: address,
86 token_id: uint256,108 _token_id: uint256,
87 data: bytes,109 _data: bytes,
88 value: value,110 _value: value,
89 ) -> Result<void, Self::Error>;111 ) -> Result<void> {
112 // TODO: Not implemetable
113 Err("not implemented".into())
114 }
90 fn safe_transfer_from(115 fn safe_transfer_from(
91 &mut self,116 &mut self,
92 from: address,117 _from: address,
93 to: address,118 _to: address,
94 token_id: uint256,119 _token_id: uint256,
95 value: value,120 _value: value,
96 ) -> Result<void, Self::Error>;121 ) -> Result<void> {
122 // TODO: Not implemetable
123 Err("not implemented".into())
124 }
97125
98 fn transfer_from(126 fn transfer_from(
99 &mut self,127 &mut self,
100 caller: caller,128 caller: caller,
101 from: address,129 from: address,
102 to: address,130 to: address,
103 token_id: uint256,131 token_id: uint256,
104 value: value,132 _value: value,
105 ) -> Result<void, Self::Error>;133 ) -> Result<void> {
134 let caller = T::CrossAccountId::from_eth(caller);
135 let from = T::CrossAccountId::from_eth(from);
136 let to = T::CrossAccountId::from_eth(to);
137 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
138
139 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)
140 .map_err(|_| "transferFrom error")?;
141 Ok(())
142 }
143
106 fn approve(144 fn approve(
107 &mut self,145 &mut self,
108 caller: caller,146 caller: caller,
109 approved: address,147 approved: address,
110 token_id: uint256,148 token_id: uint256,
111 value: value,149 _value: value,
112 ) -> Result<void, Self::Error>;150 ) -> Result<void> {
151 let caller = T::CrossAccountId::from_eth(caller);
152 let approved = T::CrossAccountId::from_eth(approved);
153 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
154
155 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)
156 .map_err(|_| "approve internal")?;
157 Ok(())
158 }
159
113 fn set_approval_for_all(160 fn set_approval_for_all(
114 &mut self,161 &mut self,
115 caller: caller,162 _caller: caller,
116 operator: address,163 _operator: address,
117 approved: bool,164 _approved: bool,
118 ) -> Result<void, Self::Error>;165 ) -> Result<void> {
166 // TODO: Not implemetable
167 Err("not implemented".into())
168 }
119169
120 fn get_approved(&self, token_id: uint256) -> Result<address, Self::Error>;170 fn get_approved(&self, _token_id: uint256) -> Result<address> {
171 // TODO: Not implemetable
172 Err("not implemented".into())
173 }
174
121 fn is_approved_for_all(175 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
122 &self,176 // TODO: Not implemetable
123 owner: address,177 Err("not implemented".into())
124 operator: address,178 }
125 ) -> Result<address, Self::Error>;
126}179}
127180
128#[solidity_interface]181#[solidity_interface(name = "ERC721UniqueExtensions")]
129pub trait ERC721UniqueExtensions {182impl<T: Config> CollectionHandle<T> {
130 type Error;183 #[solidity(rename_selector = "transfer")]
131
132 fn transfer(184 fn transfer_nft(
133 &mut self,185 &mut self,
134 caller: caller,186 caller: caller,
135 to: address,187 to: address,
136 token_id: uint256,188 token_id: uint256,
137 value: value,189 _value: value,
138 ) -> Result<void, Self::Error>;190 ) -> Result<void> {
191 let caller = T::CrossAccountId::from_eth(caller);
192 let to = T::CrossAccountId::from_eth(to);
193 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
194
195 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)
196 .map_err(|_| "transfer error")?;
197 Ok(())
198 }
139}199}
140200
141#[solidity_interface(is(201#[solidity_interface(
202 name = "UniqueNFT",
203 is(
142 ERC165,204 ERC165,
143 ERC721,205 ERC721,
144 ERC721Metadata,206 ERC721Metadata,
145 ERC721Enumerable,207 ERC721Enumerable,
146 ERC721UniqueExtensions208 ERC721UniqueExtensions
147))]209 )
148pub trait UniqueNFT {210)]
149 type Error;211impl<T: Config> CollectionHandle<T> {}
150}
151212
152#[derive(ToLog)]213#[derive(ToLog)]
153pub enum ERC20Events {214pub enum ERC20Events {
168}229}
169230
170#[solidity_interface(inline_is(InlineNameSymbol, InlineTotalSupply), events(ERC20Events))]231#[solidity_interface(
171pub trait ERC20 {232 name = "ERC20",
172 type Error;233 inline_is(InlineNameSymbol, InlineTotalSupply),
173234 events(ERC20Events)
235)]
236impl<T: Config> CollectionHandle<T> {
174 fn decimals(&self) -> Result<uint8, Self::Error>;237 fn decimals(&self) -> Result<uint8> {
238 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
239 *decimals
240 } else {
241 unreachable!()
242 })
243 }
175 fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;244 fn balance_of(&self, owner: address) -> Result<uint256> {
245 let owner = T::EvmAddressMapping::into_account_id(owner);
246 let balance = <Balance<T>>::get(self.id, owner);
247 Ok(balance.into())
248 }
176 fn transfer(249 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
177 &mut self,
178 caller: caller,
179 to: address,
180 value: uint256,
181 ) -> Result<bool, Self::Error>;250 let caller = T::CrossAccountId::from_eth(caller);
251 let to = T::CrossAccountId::from_eth(to);
252 let amount = amount.try_into().map_err(|_| "amount overflow")?;
253
254 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)
255 .map_err(|_| "transfer error")?;
256 Ok(true)
257 }
258 #[solidity(rename_selector = "transferFrom")]
182 fn transfer_from(259 fn transfer_from_fungible(
183 &mut self,260 &mut self,
184 caller: caller,261 caller: caller,
185 from: address,262 from: address,
186 to: address,263 to: address,
187 value: uint256,264 amount: uint256,
188 ) -> Result<bool, Self::Error>;265 ) -> Result<bool> {
266 let caller = T::CrossAccountId::from_eth(caller);
267 let from = T::CrossAccountId::from_eth(from);
268 let to = T::CrossAccountId::from_eth(to);
269 let amount = amount.try_into().map_err(|_| "amount overflow")?;
270
271 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)
272 .map_err(|_| "transferFrom error")?;
273 Ok(true)
274 }
275 #[solidity(rename_selector = "approve")]
189 fn approve(276 fn approve_fungible(
190 &mut self,277 &mut self,
191 caller: caller,278 caller: caller,
192 spender: address,279 spender: address,
193 value: uint256,280 amount: uint256,
194 ) -> Result<bool, Self::Error>;281 ) -> Result<bool> {
282 let caller = T::CrossAccountId::from_eth(caller);
283 let spender = T::CrossAccountId::from_eth(spender);
284 let amount = amount.try_into().map_err(|_| "amount overflow")?;
285
286 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)
287 .map_err(|_| "approve internal")?;
288 Ok(true)
289 }
195 fn allowance(&self, owner: address, spender: address) -> Result<uint256, Self::Error>;290 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
196}291 let owner = T::CrossAccountId::from_eth(owner);
292 let spender = T::CrossAccountId::from_eth(spender);
293
294 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())
295 }
296}
197297
198#[solidity_interface(is(ERC165, ERC20))]298#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
199pub trait UniqueFungible {299impl<T: Config> CollectionHandle<T> {}
200 type Error;
201}
202300
deletedpallets/nft/src/eth/erc_impl.rsdiffbeforeafterboth

no changes

deletedpallets/nft/src/eth/log.rsdiffbeforeafterboth

no changes

modifiedpallets/nft/src/eth/mod.rsdiffbeforeafterboth
1pub mod account;1pub mod account;
2pub mod erc;2pub mod erc;
3mod erc_impl;
4pub mod log;
5pub mod sponsoring;3pub mod sponsoring;
64
7use evm_coder::abi::AbiWriter;5use pallet_evm_coder_substrate::call_internal;
8use evm_coder::abi::StringError;
9use sp_std::prelude::*;
10use sp_std::borrow::ToOwned;6use sp_std::borrow::ToOwned;
11use sp_std::vec::Vec;7use sp_std::vec::Vec;
12
13use pallet_evm::{PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};8use pallet_evm::{PrecompileOutput};
14use sp_core::{H160, U256};9use sp_core::{H160, U256};
15use frame_support::storage::StorageMap;10use frame_support::storage::StorageMap;
16
17use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};11use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};
18
19use erc::{UniqueFungible, UniqueFungibleCall, UniqueNFT, UniqueNFTCall};12use erc::{UniqueFungibleCall, UniqueNFTCall};
20use evm_coder::{types::*, abi::AbiReader};
2113
22pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);14pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);
2315
42 H160(out)34 H160(out)
43}35}
4436
45fn call_internal<T: Config>(37/*
46 collection: &mut CollectionHandle<T>,38fn call_internal<T: Config>(
47 caller: caller,39 collection: &mut CollectionHandle<T>,
48 method_id: u32,40 caller: caller,
49 mut input: AbiReader,41 method_id: u32,
50 value: U256,42 mut input: AbiReader,
51) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {43 value: U256,
52 match collection.mode.clone() {44) -> Result<Option<AbiWriter>> {
53 CollectionMode::Fungible(_) => {45 match collection.mode.clone() {
54 #[cfg(feature = "std")]46 CollectionMode::Fungible(_) => {
55 {47 call_internal();
56 println!("Parse fungible call {:x}", method_id);48 let call = match UniqueFungibleCall::parse(method_id, &mut input)? {
57 }49 Some(v) => v,
58 let call = match UniqueFungibleCall::parse(method_id, &mut input)? {50 None => {
59 Some(v) => v,51 #[cfg(feature = "std")]
60 None => {52 {
61 #[cfg(feature = "std")]53 println!("Method not found");
62 {54 }
63 println!("Method not found");55 return Ok(None);
64 }56 }
65 return Ok(None);57 };
66 }58 Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(
67 };59 collection,
68 #[cfg(feature = "std")]60 Msg {
69 {61 call,
70 dbg!(&call);62 caller,
71 }63 value,
72 Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(64 },
73 collection,65 )?))
74 Msg {66 }
75 call,67 CollectionMode::NFT => {
76 caller,68 let call = match UniqueNFTCall::parse(method_id, &mut input)? {
77 value,69 Some(v) => v,
78 },70 None => return Ok(None),
79 )?))71 };
80 }72 Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(
81 CollectionMode::NFT => {73 collection,
82 let call = match UniqueNFTCall::parse(method_id, &mut input)? {74 Msg {
83 Some(v) => v,75 call,
84 None => return Ok(None),76 caller,
85 };77 value,
86 Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(78 },
87 collection,79 )?))
88 Msg {80 }
89 call,81 _ => Err("erc calls only supported to fungible and nft collections for now".into()),
90 caller,82 }
91 value,83}*/
92 },
93 )?))
94 }
95 _ => Err(StringError::from(
96 "erc calls only supported to fungible and nft collections for now",
97 )),
98 }
99}
10084
101impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {85impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {
102 fn is_reserved(target: &H160) -> bool {86 fn is_reserved(target: &H160) -> bool {
129 ) -> Option<PrecompileOutput> {113 ) -> Option<PrecompileOutput> {
130 let mut collection = map_eth_to_id(target)114 let mut collection = map_eth_to_id(target)
131 .and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;115 .and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;
132 let (method_id, input) = AbiReader::new_call(input).unwrap();
133 let result = call_internal(&mut collection, *source, method_id, input, value);116 let result = match collection.mode {
134 let cost = gas_limit - collection.gas_left();117 CollectionMode::NFT => {
118 call_internal::<UniqueNFTCall, _>(*source, &mut collection, value, input)
119 }
120 CollectionMode::Fungible(_) => {
121 call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)
122 }
123 _ => return None,
124 };
135 let logs = collection.logs.retrieve_logs();125 collection.recorder.evm_to_precompile_output(result)
136 match result {
137 Ok(Some(v)) => Some(PrecompileOutput {
138 exit_status: ExitReason::Succeed(ExitSucceed::Returned),
139 cost,
140 logs,
141 output: v.finish(),
142 }),
143 Ok(None) => None,
144 Err(e) => Some(PrecompileOutput {
145 exit_status: ExitReason::Revert(ExitRevert::Reverted),
146 cost: 0,
147 logs: Default::default(),
148 output: AbiWriter::from(e).finish(),
149 }),
150 }
151 }126 }
152}127}
153
154// TODO: This function is slow, and output can be memoized
155pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {
156 // FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728
157 #[cfg(feature = "std")]
158 {
159 let contract = collection_id_to_address(collection_id);
160 let signed = ethereum_tx_sign::RawTransaction {
161 nonce: 0.into(),
162 to: Some(contract.0.into()),
163 value: 0.into(),
164 gas_price: 0.into(),
165 gas: 0.into(),
166 // zero selector, this transaction always have same sender, so all data should be acquired from logs
167 data: Vec::from([0, 0, 0, 0]),
168 }
169 .sign(
170 // TODO: move to pallet config
171 // 0xF70631E55faff9f3FD3681545aa6c724226a3853
172 // 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a
173 &hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a")
174 .into(),
175 &chain_id,
176 );
177 rlp::decode::<ethereum::Transaction>(&signed)
178 .expect("transaction is just created, it can't be broken")
179 }
180 #[cfg(not(feature = "std"))]
181 {
182 panic!("transaction generation not yet supported by wasm runtime while generating transaction for collection_id {}, chain_id {}", collection_id, chain_id)
183 }
184}
185128
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
36use sp_std::vec;36use sp_std::vec;
37use sp_runtime::sp_std::prelude::Vec;37use sp_runtime::sp_std::prelude::Vec;
38use core::ops::{Deref, DerefMut};38use core::ops::{Deref, DerefMut};
39use core::cell::RefCell;
40use nft_data_structs::{39use nft_data_structs::{
41 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,
42 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,41 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,
43 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,
44 FungibleItemType, ReFungibleItemType,43 FungibleItemType, ReFungibleItemType,
45};44};
46use pallet_ethereum::EthereumTransactionSender;
4745
48#[cfg(test)]46#[cfg(test)]
49mod mock;47mod mock;
55mod eth;53mod eth;
56mod sponsorship;54mod sponsorship;
57pub use sponsorship::NftSponsorshipHandler;55pub use sponsorship::NftSponsorshipHandler;
56pub use eth::sponsoring::NftEthSponsorshipHandler;
5857
59pub use eth::NftErcSupport;58pub use eth::NftErcSupport;
60pub use eth::account::*;59pub use eth::account::*;
178 }177 }
179}178}
180179
180#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
181pub struct CollectionHandle<T: Config> {181pub struct CollectionHandle<T: Config> {
182 pub id: CollectionId,182 pub id: CollectionId,
183 collection: Collection<T>,183 collection: Collection<T>,
184 logs: eth::log::LogRecorder,184 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
185 evm_address: H160,
186 gas_limit: RefCell<u64>,
187}185}
188impl<T: Config> CollectionHandle<T> {186impl<T: Config> CollectionHandle<T> {
189 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {187 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
190 <CollectionById<T>>::get(id).map(|collection| Self {188 <CollectionById<T>>::get(id).map(|collection| Self {
191 id,189 id,
192 collection,190 collection,
193 logs: eth::log::LogRecorder::default(),
194 evm_address: eth::collection_id_to_address(id),191 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(
195 gas_limit: RefCell::new(gas_limit),192 eth::collection_id_to_address(id),
193 gas_limit,
194 ),
196 })195 })
197 }196 }
198 pub fn get(id: CollectionId) -> Option<Self> {197 pub fn get(id: CollectionId) -> Option<Self> {
199 Self::get_with_gas_limit(id, u64::MAX)198 Self::get_with_gas_limit(id, u64::MAX)
200 }199 }
201 pub fn gas_left(&self) -> u64 {200 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
202 *self.gas_limit.borrow()201 self.recorder.log_sub(log)
203 }202 }
204 pub fn consume_gas(&self, gas: u64) -> DispatchResult {203 fn consume_gas(&self, gas: u64) -> DispatchResult {
205 let mut gas_limit = self.gas_limit.borrow_mut();204 self.recorder.consume_gas_sub(gas)
206 if *gas_limit < gas {
207 fail!(Error::<T>::OutOfGas);
208 }
209 *gas_limit -= gas;
210 Ok(())
211 }205 }
212 pub fn log(&self, log: impl evm_coder::ToLog) {206 pub fn submit_logs(self) -> DispatchResult {
213 self.logs.log(log.to_log(self.evm_address))207 self.recorder.submit_logs()
214 }208 }
215 pub fn into_inner(self) -> Collection<T> {209 pub fn save(self) -> DispatchResult {
210 self.recorder.submit_logs()?;
216 self.collection211 <CollectionById<T>>::insert(self.id, self.collection);
217 }212 Ok(())
213 }
218}214}
219impl<T: Config> Deref for CollectionHandle<T> {215impl<T: Config> Deref for CollectionHandle<T> {
220 type Target = Collection<T>;216 type Target = Collection<T>;
230 }226 }
231}227}
232228
233pub trait Config: system::Config + Sized {229pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {
234 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;230 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
235231
236 /// Weight information for extrinsics in this pallet.232 /// Weight information for extrinsics in this pallet.
246 >;242 >;
247 type TreasuryAccountId: Get<Self::AccountId>;243 type TreasuryAccountId: Get<Self::AccountId>;
248
249 type EthereumChainId: Get<u64>;
250 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;
251}244}
252245
253// # Used definitions246// # Used definitions
564 fail!(Error::<T>::NoPermission);557 fail!(Error::<T>::NoPermission);
565 }558 }
566559
567 <AddressTokens<T>>::remove_prefix(collection_id);560 <AddressTokens<T>>::remove_prefix(collection_id, None);
568 <Allowances<T>>::remove_prefix(collection_id);561 <Allowances<T>>::remove_prefix(collection_id, None);
569 <Balance<T>>::remove_prefix(collection_id);562 <Balance<T>>::remove_prefix(collection_id, None);
570 <ItemListIndex>::remove(collection_id);563 <ItemListIndex>::remove(collection_id);
571 <AdminList<T>>::remove(collection_id);564 <AdminList<T>>::remove(collection_id);
572 <CollectionById<T>>::remove(collection_id);565 <CollectionById<T>>::remove(collection_id);
573 <WhiteList<T>>::remove_prefix(collection_id);566 <WhiteList<T>>::remove_prefix(collection_id, None);
574567
575 <NftItemList<T>>::remove_prefix(collection_id);568 <NftItemList<T>>::remove_prefix(collection_id, None);
576 <FungibleItemList<T>>::remove_prefix(collection_id);569 <FungibleItemList<T>>::remove_prefix(collection_id, None);
577 <ReFungibleItemList<T>>::remove_prefix(collection_id);570 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);
578571
579 <NftTransferBasket<T>>::remove_prefix(collection_id);572 <NftTransferBasket<T>>::remove_prefix(collection_id, None);
580 <FungibleTransferBasket<T>>::remove_prefix(collection_id);573 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
581 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);574 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);
582575
583 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);576 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
584577
585 DestroyedCollectionCount::put(DestroyedCollectionCount::get()578 DestroyedCollectionCount::put(DestroyedCollectionCount::get()
586 .checked_add(1)579 .checked_add(1)
667 let mut target_collection = Self::get_collection(collection_id)?;660 let mut target_collection = Self::get_collection(collection_id)?;
668 Self::check_owner_permissions(&target_collection, &sender)?;661 Self::check_owner_permissions(&target_collection, &sender)?;
669 target_collection.access = mode;662 target_collection.access = mode;
670 Self::save_collection(target_collection);663 target_collection.save()
671
672 Ok(())
673 }664 }
674665
675 /// Allows Anyone to create tokens if:666 /// Allows Anyone to create tokens if:
694 let mut target_collection = Self::get_collection(collection_id)?;685 let mut target_collection = Self::get_collection(collection_id)?;
695 Self::check_owner_permissions(&target_collection, &sender)?;686 Self::check_owner_permissions(&target_collection, &sender)?;
696 target_collection.mint_mode = mint_permission;687 target_collection.mint_mode = mint_permission;
697 Self::save_collection(target_collection);688 target_collection.save()
698
699 Ok(())
700 }689 }
701690
702 /// Change the owner of the collection.691 /// Change the owner of the collection.
718 let mut target_collection = Self::get_collection(collection_id)?;707 let mut target_collection = Self::get_collection(collection_id)?;
719 Self::check_owner_permissions(&target_collection, &sender)?;708 Self::check_owner_permissions(&target_collection, &sender)?;
720 target_collection.owner = new_owner;709 target_collection.owner = new_owner;
721 Self::save_collection(target_collection);710 target_collection.save()
722
723 Ok(())
724 }711 }
725712
726 /// Adds an admin of the Collection.713 /// Adds an admin of the Collection.
800 Self::check_owner_permissions(&target_collection, &sender)?;787 Self::check_owner_permissions(&target_collection, &sender)?;
801788
802 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);789 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);
803 Self::save_collection(target_collection);790 target_collection.save()
804
805 Ok(())
806 }791 }
807792
808 /// # Permissions793 /// # Permissions
824 );809 );
825810
826 target_collection.sponsorship = SponsorshipState::Confirmed(sender);811 target_collection.sponsorship = SponsorshipState::Confirmed(sender);
827 Self::save_collection(target_collection);812 target_collection.save()
828
829 Ok(())
830 }813 }
831814
832 /// Switch back to pay-per-own-transaction model.815 /// Switch back to pay-per-own-transaction model.
847 Self::check_owner_permissions(&target_collection, &sender)?;830 Self::check_owner_permissions(&target_collection, &sender)?;
848831
849 target_collection.sponsorship = SponsorshipState::Disabled;832 target_collection.sponsorship = SponsorshipState::Disabled;
850 Self::save_collection(target_collection);833 target_collection.save()
851
852 Ok(())
853 }834 }
854835
855 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.836 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.
884865
885 Self::create_item_internal(&sender, &collection, &owner, data)?;866 Self::create_item_internal(&sender, &collection, &owner, data)?;
886867
887 Self::submit_logs(collection)?;868 collection.submit_logs()
888 Ok(())
889 }869 }
890870
891 /// This method creates multiple items in a collection created with CreateCollection method.871 /// This method creates multiple items in a collection created with CreateCollection method.
918898
919 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;899 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;
920900
921 Self::submit_logs(collection)?;901 collection.submit_logs()
922 Ok(())
923 }902 }
924903
925 /// Destroys a concrete instance of NFT.904 /// Destroys a concrete instance of NFT.
944923
945 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;924 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;
946925
947 Self::submit_logs(target_collection)?;926 target_collection.submit_logs()
948 Ok(())
949 }927 }
950928
951 /// Change ownership of the token.929 /// Change ownership of the token.
979957
980 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;958 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;
981959
982 Self::submit_logs(collection)?;960 collection.submit_logs()
983 Ok(())
984 }961 }
985962
986 /// Set, change, or remove approved address to transfer the ownership of the NFT.963 /// Set, change, or remove approved address to transfer the ownership of the NFT.
1006983
1007 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;984 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;
1008985
1009 Self::submit_logs(collection)?;986 collection.submit_logs()
1010 Ok(())
1011 }987 }
1012988
1013 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.989 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
10371013
1038 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;1014 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;
10391015
1040 Self::submit_logs(collection)?;1016 collection.submit_logs()
1041 Ok(())
1042 }1017 }
1043 // #[weight = 0]1018 // #[weight = 0]
1044 // // let no_perm_mes = "You do not have permissions to modify this collection";1019 // // let no_perm_mes = "You do not have permissions to modify this collection";
1107 let mut target_collection = Self::get_collection(collection_id)?;1082 let mut target_collection = Self::get_collection(collection_id)?;
1108 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1083 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;
1109 target_collection.schema_version = version;1084 target_collection.schema_version = version;
1110 Self::save_collection(target_collection);1085 target_collection.save()
1111
1112 Ok(())
1113 }1086 }
11141087
1115 /// Set off-chain data schema.1088 /// Set off-chain data schema.
1139 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");1112 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");
11401113
1141 target_collection.offchain_schema = schema;1114 target_collection.offchain_schema = schema;
1142 Self::save_collection(target_collection);1115 target_collection.save()
1143
1144 Ok(())
1145 }1116 }
11461117
1147 /// Set const on-chain data schema.1118 /// Set const on-chain data schema.
1171 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");1142 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");
11721143
1173 target_collection.const_on_chain_schema = schema;1144 target_collection.const_on_chain_schema = schema;
1174 Self::save_collection(target_collection);1145 target_collection.save()
1175
1176 Ok(())
1177 }1146 }
11781147
1179 /// Set variable on-chain data schema.1148 /// Set variable on-chain data schema.
1203 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");1172 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");
12041173
1205 target_collection.variable_on_chain_schema = schema;1174 target_collection.variable_on_chain_schema = schema;
1206 Self::save_collection(target_collection);1175 target_collection.save()
1207
1208 Ok(())
1209 }1176 }
12101177
1211 // Sudo permissions function1178 // Sudo permissions function
12541221
1255 target_collection.limits = new_limits;1222 target_collection.limits = new_limits;
1223
1256 Self::save_collection(target_collection);1224 target_collection.save()
1257
1258 Ok(())
1259 }1225 }
1260 }1226 }
1261}1227}
1376 owner: *sender.as_eth(),1342 owner: *sender.as_eth(),
1377 approved: *spender.as_eth(),1343 approved: *spender.as_eth(),
1378 token_id: item_id.into(),1344 token_id: item_id.into(),
1379 });1345 })?;
1380 }1346 }
13811347
1382 if matches!(collection.mode, CollectionMode::Fungible(_)) {1348 if matches!(collection.mode, CollectionMode::Fungible(_)) {
1385 owner: *sender.as_eth(),1351 owner: *sender.as_eth(),
1386 spender: *spender.as_eth(),1352 spender: *spender.as_eth(),
1387 value: allowance.into(),1353 value: allowance.into(),
1388 });1354 })?;
1389 }1355 }
13901356
1391 Self::deposit_event(RawEvent::Approved(1357 Self::deposit_event(RawEvent::Approved(
1461 owner: *from.as_eth(),1427 owner: *from.as_eth(),
1462 spender: *sender.as_eth(),1428 spender: *sender.as_eth(),
1463 value: allowance.into(),1429 value: allowance.into(),
1464 });1430 })?;
1465 }1431 }
14661432
1467 Ok(())1433 Ok(())
1789 from: H160::default(),1755 from: H160::default(),
1790 to: *item_owner.as_eth(),1756 to: *item_owner.as_eth(),
1791 token_id: current_index.into(),1757 token_id: current_index.into(),
1792 });1758 })?;
1793 Self::deposit_event(RawEvent::ItemCreated(1759 Self::deposit_event(RawEvent::ItemCreated(
1794 collection_id,1760 collection_id,
1795 current_index,1761 current_index,
1886 from: *owner.as_eth(),1852 from: *owner.as_eth(),
1887 to: H160::default(),1853 to: H160::default(),
1888 value: value.into(),1854 value: value.into(),
1889 });1855 })?;
1890 Ok(())1856 Ok(())
1891 }1857 }
18921858
1896 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1862 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)
1897 }1863 }
1898
1899 fn save_collection(collection: CollectionHandle<T>) {
1900 <CollectionById<T>>::insert(collection.id, collection.into_inner());
1901 }
1902
1903 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {
1904 if collection.logs.is_empty() {
1905 return Ok(());
1906 }
1907 T::EthereumTransactionSender::submit_logs_transaction(
1908 eth::generate_transaction(collection.id, T::EthereumChainId::get()),
1909 collection.logs.retrieve_logs(),
1910 )
1911 }
19121864
1913 fn check_owner_permissions(1865 fn check_owner_permissions(
1914 target_collection: &CollectionHandle<T>,1866 target_collection: &CollectionHandle<T>,
2037 from: *owner.as_eth(),1989 from: *owner.as_eth(),
2038 to: *recipient.as_eth(),1990 to: *recipient.as_eth(),
2039 value: value.into(),1991 value: value.into(),
2040 });1992 })?;
2041 Self::deposit_event(RawEvent::Transfer(1993 Self::deposit_event(RawEvent::Transfer(
2042 collection.id,1994 collection.id,
2043 1,1995 1,
2173 from: *sender.as_eth(),2125 from: *sender.as_eth(),
2174 to: *new_owner.as_eth(),2126 to: *new_owner.as_eth(),
2175 token_id: item_id.into(),2127 token_id: item_id.into(),
2176 });2128 })?;
2177 Self::deposit_event(RawEvent::Transfer(2129 Self::deposit_event(RawEvent::Transfer(
2178 collection.id,2130 collection.id,
2179 item_id,2131 item_id,
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
193 type EvmAddressMapping = TestEvmAddressMapping;193 type EvmAddressMapping = TestEvmAddressMapping;
194 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;194 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
195 type CrossAccountId = TestCrossAccountId;195 type CrossAccountId = TestCrossAccountId;
196 type EthereumChainId = EthereumChainId;
197 type EthereumTransactionSender = TestEtheremTransactionSender;196 type EthereumTransactionSender = TestEtheremTransactionSender;
198}197}
199198