Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Res/nft pallet #67

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch =
pallet-session = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false, features = ["historical"] }
pallet-sudo = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
pallet-grandpa = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
pallet-nfts = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v1.0.0", default-features = false }
Expand Down
131 changes: 94 additions & 37 deletions ownership-chain/precompile/erc721/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
#![cfg_attr(not(feature = "std"), no_std)]
use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput};
use pallet_living_assets_ownership::{address_to_collection_id, CollectionId};
use frame_support::traits::tokens::nonfungibles_v2::{Create, Inspect, Mutate, Transfer};
use pallet_living_assets_ownership::address_to_collection_id;
use precompile_utils::{
keccak256, revert, succeed, Address, Bytes, EvmDataWriter, EvmResult, FunctionModifier, LogExt,
LogsBuilder, PrecompileHandleExt,
keccak256, revert, revert_dispatch_error, succeed, Address, Bytes, EvmDataWriter, EvmResult,
FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt,
};

use ownership_parachain_primitives::nfts::*;
use parity_scale_codec::alloc::string::ToString;
use sp_core::{H160, H256, U256};
use sp_runtime::traits::Convert;
use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec};

/// Represents a mapping between `AssetId` and `AccountId`.
/// This struct provides functionalities to convert an `AssetId` (represented by `U256`) into an
/// `AccountId`.
pub struct AssetIdToInitialOwner;
impl Convert<U256, H160> for AssetIdToInitialOwner {
fn convert(asset_id: U256) -> H160 {
let mut bytes = [0u8; 20];
let asset_id_bytes: [u8; 32] = asset_id.into();
bytes.copy_from_slice(&asset_id_bytes[asset_id_bytes.len() - 20..]);

bytes.into()
}
}

/// Solidity selector of the TransferFrom log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_TRANSFER_FROM: [u8; 32] = keccak256!("Transfer(address,address,uint256)");

Expand All @@ -24,17 +42,26 @@ pub enum Action {
}

/// Wrapper for the precompile function.
pub struct Erc721Precompile<AccountId, AssetManager>(PhantomData<(AccountId, AssetManager)>);
pub struct Erc721Precompile<AccountId, CollectionId, ItemId, AssetManager>(
PhantomData<(AccountId, CollectionId, ItemId, AssetManager)>,
);

impl<AccountId, AssetManager> Precompile for Erc721Precompile<AccountId, AssetManager>
impl<AccountId, CollectionId, ItemId, AssetManager> Precompile
for Erc721Precompile<AccountId, CollectionId, ItemId, AssetManager>
where
AccountId: Into<H160> + From<H160> + Into<[u8; 20]>,
AssetManager: pallet_living_assets_ownership::traits::Erc721<AccountId>,
AccountId: Into<H160> + From<H160> + Into<[u8; 20]> + PartialEq,
CollectionId: From<u64> + Into<u64>,
ItemId: From<U256> + Into<U256>,
AssetManager: Create<AccountId, CollectionConfig>
+ Mutate<AccountId, ItemConfig>
+ Inspect<AccountId, ItemId = ItemId, CollectionId = CollectionId>
+ Transfer<AccountId>,
{
fn execute(handle: &mut impl PrecompileHandle) -> EvmResult<PrecompileOutput> {
// collection id is encoded into the contract address
let collection_id = address_to_collection_id(handle.code_address())
.map_err(|_| revert("invalid collection address"))?;
.map_err(|_| revert("invalid collection address"))?
.into();

let selector = handle.read_selector()?;

Expand All @@ -52,10 +79,16 @@ where
}
}

impl<AccountId, AssetManager> Erc721Precompile<AccountId, AssetManager>
impl<AccountId, CollectionId, ItemId, AssetManager>
Erc721Precompile<AccountId, CollectionId, ItemId, AssetManager>
where
AccountId: Into<H160> + From<H160> + Into<[u8; 20]>,
AssetManager: pallet_living_assets_ownership::traits::Erc721<AccountId>,
AccountId: Into<H160> + From<H160> + Into<[u8; 20]> + PartialEq,
CollectionId: From<u64> + Into<u64>,
ItemId: From<U256> + Into<U256>,
AssetManager: Create<AccountId, CollectionConfig>
+ Mutate<AccountId, ItemConfig>
+ Inspect<AccountId, ItemId = ItemId, CollectionId = CollectionId>
+ Transfer<AccountId>,
{
fn owner_of(
collection_id: CollectionId,
Expand All @@ -66,9 +99,12 @@ where

let asset_id: U256 = input.read()?;

let owner: H160 = AssetManager::owner_of(collection_id, asset_id).map_err(revert)?.into();

Ok(succeed(EvmDataWriter::new().write(Address(owner)).build()))
let owner = if let Some(owner) = AssetManager::owner(&collection_id, &asset_id.into()) {
owner
} else {
AssetIdToInitialOwner::convert(asset_id.clone()).into()
};
Ok(succeed(EvmDataWriter::new().write(Address(owner.into())).build()))
}

fn token_uri(
Expand All @@ -80,8 +116,15 @@ where

let asset_id: U256 = input.read()?;

let uri = AssetManager::token_uri(collection_id, asset_id).map_err(revert)?;
Ok(succeed(EvmDataWriter::new().write(Bytes(uri)).build()))
let base_uri = AssetManager::collection_attribute(&collection_id, b"baseURI")
.ok_or(revert("no base URI set"))?;

// concatenate base_uri with asset_id
let mut token_uri = base_uri.to_vec();
token_uri.push(b'/');
token_uri.extend_from_slice(asset_id.to_string().as_bytes());

Ok(succeed(EvmDataWriter::new().write(Bytes(token_uri)).build()))
}

fn transfer_from(
Expand All @@ -93,30 +136,44 @@ where
input.expect_arguments(3)?;
let from = input.read::<Address>()?;
let to = input.read::<Address>()?;
let asset_id: pallet_living_assets_ownership::AssetId = input.read()?;
let asset_id: U256 = input.read()?;
let mut asset_id_big_endian = [0u8; 32];
asset_id.to_big_endian(&mut asset_id_big_endian);

AssetManager::transfer_from(
handle.context().caller.into(),
collection_id,
from.0.into(),
to.0.into(),
asset_id,
)
.map_err(revert)?;

LogsBuilder::new(handle.context().address)
.log4(
SELECTOR_LOG_TRANSFER_FROM,
from.0,
to.0,
H256::from_slice(asset_id_big_endian.as_slice()),
Vec::new(),
)
.record(handle)?;

Ok(succeed(vec![]))
// validate dest
if to.0 == H160::zero() {
return Err(revert("cannot transfer to zero address"))
}
if to.0 == from.0 {
return Err(revert("cannot transfer to self"))
}

// validate owner here
let who = handle.context().caller.into();
let owner = if let Some(owner) = AssetManager::owner(&collection_id, &asset_id.into()) {
owner
} else {
AssetIdToInitialOwner::convert(asset_id.clone()).into()
};

if owner == who {
AssetManager::transfer(&collection_id, &asset_id.into(), &to.0.into())
.map_err(revert_dispatch_error)?;

LogsBuilder::new(handle.context().address)
.log4(
SELECTOR_LOG_TRANSFER_FROM,
from.0,
to.0,
H256::from_slice(asset_id_big_endian.as_slice()),
Vec::new(),
)
.record(handle)?;

Ok(succeed(vec![]))
} else {
Err(revert("not owner"))
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions ownership-chain/precompile/living-assets/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ pallet-evm = { workspace = true }

# Substrate
frame-support = { workspace = true }
pallet-nfts = { workspace = true }
sp-arithmetic = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
sp-std = { workspace = true }

# Local pallet
ownership-parachain-primitives = { workspace = true }
pallet-living-assets-ownership = { workspace = true }

# Utils
Expand Down Expand Up @@ -56,4 +58,5 @@ std = [
"precompile-utils/std",
"parity-scale-codec/std",
"scale-info/std",
"pallet-nfts/std",
]
Loading
Loading