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

Implementing Asset Evolution Feature in Laos #125

Merged
merged 38 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a2fc3de
ownership chain has pallet evolution
asiniscalchi Oct 26, 2023
72e554e
using evolution precompiled
asiniscalchi Oct 26, 2023
d8da80f
fmt
asiniscalchi Oct 26, 2023
94d6a2e
add test
asiniscalchi Oct 26, 2023
13e32c5
evm evolution precomoile moved to ownership
asiniscalchi Oct 26, 2023
33936ed
moved evolution pallet to ownership chain
asiniscalchi Oct 26, 2023
d5a98c3
Merge branch 'main' into feature/move_evo_logic_into_caladan
asiniscalchi Oct 26, 2023
4e8ce8a
add evolve_with_external_uri fn and first test
magecnion Oct 26, 2023
679fdc6
fix clippy
asiniscalchi Oct 26, 2023
379d090
Merge branch 'feature/move_evo_logic_into_caladan' of github.com:free…
asiniscalchi Oct 26, 2023
1c26f4d
finish pallet implementation
magecnion Oct 26, 2023
0ab8c7a
Merge branch 'feature/move_evo_logic_into_caladan' of github.com:free…
magecnion Oct 26, 2023
ad07e91
WIP add evolve to precompiles
magecnion Oct 26, 2023
5008470
add MetadataUpdate test
magecnion Oct 27, 2023
20fd0e2
clean
magecnion Oct 27, 2023
6c1fe76
Merge branch 'main' of github.com:freeverseio/laos into feature/evolv…
magecnion Oct 27, 2023
0b95bb7
fix signature fn LaosEvolution.sol file
magecnion Oct 27, 2023
98cbece
add token uri to evolve event
magecnion Oct 27, 2023
6025a7f
fix events log
magecnion Oct 27, 2023
74bcb4c
typos in solidity LaosEvolution.sol
magecnion Oct 27, 2023
f74df46
fix typo in solidity contracts
magecnion Oct 27, 2023
976dd40
typos in solidity LaosEvolution.sol
magecnion Oct 27, 2023
72aa439
fix function selectors
magecnion Oct 27, 2023
21f9b12
EvolvedWithExternalTokenURI => MetadataUpdate
magecnion Oct 27, 2023
7d25016
make token_id as indexed
magecnion Oct 30, 2023
d700736
Merge branch 'main' of github.com:freeverseio/laos into feature/evolv…
magecnion Oct 30, 2023
6d8abaf
fix signature
magecnion Oct 30, 2023
dd01c43
fix signature
magecnion Oct 30, 2023
2600c11
fix signature
magecnion Oct 30, 2023
8980eb3
add topic to event
magecnion Oct 30, 2023
be9f922
include return type
magecnion Oct 30, 2023
a0c31ea
improve test readbility
magecnion Oct 30, 2023
898ccb3
change requested: unify solidity interface events
magecnion Oct 30, 2023
20883ff
change requested: use Bytes()
magecnion Oct 30, 2023
f1d4ca5
Merge branch 'main' into feature/evolve_assets
asiniscalchi Oct 31, 2023
aad8493
Merge branch 'main' of github.com:freeverseio/laos into feature/evolv…
magecnion Oct 31, 2023
ca6c3db
fix test
magecnion Oct 31, 2023
6322841
Merge branch 'feature/evolve_assets' of github.com:freeverseio/laos i…
magecnion Oct 31, 2023
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
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "r

# LAOS pallets
pallet-living-assets-ownership = { path = "./ownership-chain/pallets/living-assets-ownership", default-features = false }

pallet-laos-evolution = { path = "./ownership-chain/pallets/laos-evolution", default-features = false }
pallet-evm-living-assets-ownership = { path = "./ownership-chain/precompile/living-assets", default-features = false }
pallet-evm-laos-evolution = { path = "./ownership-chain/precompile/laos-evolution", default-features = false }
Expand Down
36 changes: 34 additions & 2 deletions ownership-chain/pallets/laos-evolution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,20 @@ pub mod pallet {
CollectionCreated { collection_id: CollectionId, owner: AccountIdOf<T> },
/// Asset minted
/// [collection_id, slot, to, token_uri]
MintedWithExternalTokenURI {
MintedWithExternalURI {
collection_id: CollectionId,
slot: Slot,
to: AccountIdOf<T>,
token_uri: TokenUriOf<T>,
token_id: TokenId,
},
/// Asset evolved
/// [collection_id, token_uri, token_id]
EvolvedWithExternalURI {
collection_id: CollectionId,
token_id: TokenId,
token_uri: TokenUriOf<T>,
},
}

// Errors inform users that something went wrong.
Expand All @@ -95,6 +102,8 @@ pub mod pallet {
AlreadyMinted,
/// This happens when `Slot` is larger than 96 bits
SlotOverflow,
/// Asset does not exist
AssetDoesNotExist,
}

#[pallet::call]
Expand Down Expand Up @@ -140,7 +149,7 @@ impl<T: Config> LaosEvolution<AccountIdOf<T>, TokenUriOf<T>> for Pallet<T> {

TokenURI::<T>::insert(collection_id, token_id, token_uri.clone());

Self::deposit_event(Event::MintedWithExternalTokenURI {
Self::deposit_event(Event::MintedWithExternalURI {
collection_id,
slot,
to,
Expand All @@ -158,6 +167,29 @@ impl<T: Config> LaosEvolution<AccountIdOf<T>, TokenUriOf<T>> for Pallet<T> {
fn token_uri(collection_id: CollectionId, token_id: TokenId) -> Option<TokenUriOf<T>> {
TokenURI::<T>::get(collection_id, token_id)
}

fn evolve_with_external_uri(
who: AccountIdOf<T>,
collection_id: CollectionId,
token_id: TokenId,
token_uri: TokenUriOf<T>,
) -> Result<(), DispatchError> {
ensure!(
CollectionOwner::<T>::contains_key(collection_id),
Error::<T>::CollectionDoesNotExist
);
ensure!(CollectionOwner::<T>::get(collection_id) == Some(who), Error::<T>::NoPermission);
ensure!(
TokenURI::<T>::contains_key(collection_id, token_id),
Error::<T>::AssetDoesNotExist
);

TokenURI::<T>::insert(collection_id, token_id, token_uri.clone());

Self::deposit_event(Event::EvolvedWithExternalURI { collection_id, token_id, token_uri });

Ok(())
}
}

/// Converts `Slot` and `H160` to `TokenId`
Expand Down
103 changes: 102 additions & 1 deletion ownership-chain/pallets/laos-evolution/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ fn mint_with_external_uri_works() {
assert_eq!(LaosEvolution::token_uri(collection_id, token_id), Some(token_uri.clone()));

System::assert_has_event(
Event::MintedWithExternalTokenURI {
Event::MintedWithExternalURI {
collection_id,
slot,
to: owner,
Expand Down Expand Up @@ -306,3 +306,104 @@ fn token_uri_of_existent_token_returns_correct_token_uri() {
assert_eq!(LaosEvolution::token_uri(collection_id, token_id), Some(token_uri));
});
}

#[test]
fn evolve_with_external_uri_when_unexistent_collection_id_should_fail() {
new_test_ext().execute_with(|| {
let who = AccountId::from_str(ALICE).unwrap();
let collection_id = LaosEvolution::collection_counter();
let slot = 0;
let owner = AccountId::from_str(ALICE).unwrap();
let token_id = slot_and_owner_to_token_id(slot, owner).unwrap();
let new_token_uri: TokenUriOf<Test> =
vec![1, MaxTokenUriLength::get() as u8].try_into().unwrap();

assert_noop!(
LaosEvolution::evolve_with_external_uri(who, collection_id, token_id, new_token_uri),
Error::<Test>::CollectionDoesNotExist
);
});
}

#[test]
fn evolve_with_external_uri_when_sender_is_not_collection_owner_should_fail() {
new_test_ext().execute_with(|| {
let who = AccountId::from_str(ALICE).unwrap();
let owner = AccountId::from_str(BOB).unwrap();
let collection_id = create_collection(BOB);
let slot = 0;
let token_id = slot_and_owner_to_token_id(slot, owner).unwrap();
let new_token_uri: TokenUriOf<Test> =
vec![1, MaxTokenUriLength::get() as u8].try_into().unwrap();

assert_noop!(
LaosEvolution::evolve_with_external_uri(who, collection_id, token_id, new_token_uri),
Error::<Test>::NoPermission
);
});
}

#[test]
fn evolve_with_external_uri_when_asset_doesnt_exist_should_fail() {
new_test_ext().execute_with(|| {
let who = AccountId::from_str(ALICE).unwrap();
let owner = AccountId::from_str(BOB).unwrap();
let collection_id = create_collection(ALICE);
let slot = 0;
let token_id = slot_and_owner_to_token_id(slot, owner).unwrap();
let new_token_uri: TokenUriOf<Test> =
vec![1, MaxTokenUriLength::get() as u8].try_into().unwrap();

assert_noop!(
LaosEvolution::evolve_with_external_uri(who, collection_id, token_id, new_token_uri),
Error::<Test>::AssetDoesNotExist
);
});
}

#[test]
fn evolve_with_external_uri_happy_path() {
new_test_ext().execute_with(|| {
System::set_block_number(1);

let owner = AccountId::from_str(BOB).unwrap();
let collection_id = create_collection(BOB);
let slot = 0;
let token_id = slot_and_owner_to_token_id(slot, owner).unwrap();
let token_uri: TokenUriOf<Test> =
vec![1, MaxTokenUriLength::get() as u8].try_into().unwrap();
let new_token_uri: TokenUriOf<Test> =
vec![1, MaxTokenUriLength::get() as u8].try_into().unwrap();

assert_ok!(LaosEvolution::mint_with_external_uri(
owner,
collection_id,
slot,
owner,
token_uri.clone()
));
// token uri is set
assert_eq!(LaosEvolution::token_uri(collection_id, token_id), Some(token_uri.clone()));

assert_eq!(
LaosEvolution::evolve_with_external_uri(
owner,
collection_id,
token_id,
new_token_uri.clone()
),
Ok(())
);

// token uri is updated and event is emitted
assert_eq!(LaosEvolution::token_uri(collection_id, token_id), Some(new_token_uri.clone()));
System::assert_has_event(
Event::EvolvedWithExternalURI {
token_id,
collection_id,
token_uri: new_token_uri.clone(),
}
.into(),
);
});
}
8 changes: 8 additions & 0 deletions ownership-chain/pallets/laos-evolution/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ pub trait LaosEvolution<AccountId, TokenUri> {

/// Get token URI of a token in a collection
fn token_uri(collection_id: CollectionId, token_id: TokenId) -> Option<TokenUri>;

/// Evolve token with external URI
fn evolve_with_external_uri(
who: AccountId,
collection_id: CollectionId,
token_id: TokenId,
token_uri: TokenUri,
) -> Result<(), DispatchError>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ interface LaosEvolution {
/// @param to the initial owner of the newly minted token
/// @param tokenURI the URI of the newly minted token
/// @param tokenId the resulting id of the newly minted token
event MintedWithExternalTokenURI(
event MintedWithExternalURI(
uint64 collectionId,
uint96 slot,
address indexed to,
string tokenURI,
uint256 tokenId
);

/// @notice Emitted when a token metadata is updated
/// @param tokenId the id of the token for which the metadata has changed
/// @param collectionId the id of the collection
/// @param tokenURI the new URI of the token
event EvolvedWithExternalURI(
uint64 collectionId,
uint256 indexed tokenId,
string tokenURI
);

/// @notice Creates a new collection
/// @dev Call this function to create a new collection
/// @param owner the owner of the newly created collection
Expand All @@ -52,10 +62,21 @@ interface LaosEvolution {
/// @param to the owner of the newly minted token
/// @param tokenURI the tokenURI of the newly minted token
/// @return the id of the newly minted token
function mintWithExternalUri(
function mintWithExternalURI(
uint64 collectionId,
uint96 slot,
address to,
string calldata tokenURI
) external returns (uint256);

/// @notice Changes the tokenURI of an existing token
/// @dev Call this function to evolve an existing token, the caller must be the owner of the collection
/// @param collectionId the id of the collection
/// @param tokenId the id of the token
/// @param tokenURI the new tokenURI of the token
function evolveWithExternalURI(
uint64 collectionId,
uint256 tokenId,
string calldata tokenURI
) external returns (uint256);
}
48 changes: 46 additions & 2 deletions ownership-chain/precompile/laos-evolution/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec};
pub const SELECTOR_LOG_NEW_COLLECTION: [u8; 32] = keccak256!("NewCollection(uint64,address)");
/// Solidity selector of the Transfer log, which is the Keccak of the Log signature.
pub const SELECTOR_LOG_MINTED_WITH_EXTERNAL_TOKEN_URI: [u8; 32] =
keccak256!("MintedWithExternalTokenURI(uint64,uint96,address,string,uint256)");
keccak256!("MintedWithExternalURI(uint64,uint96,address,string,uint256)");
pub const SELECTOR_LOG_EVOLVED_WITH_EXTERNAL_TOKEN_URI: [u8; 32] =
keccak256!("EvolvedWithExternalURI(uint256,uint64,string)");

#[precompile_utils_macro::generate_function_selector]
#[derive(Debug, PartialEq)]
Expand All @@ -28,7 +30,9 @@ pub enum Action {
/// Get tokenURI of the token in collection
TokenURI = "tokenURI(uint64,uint256)",
/// Mint token
Mint = "mintWithExternalUri(uint64,uint96,address,string)",
Mint = "mintWithExternalURI(uint64,uint96,address,string)",
/// Evolve token
Evolve = "evolveWithExternalURI(uint64,uint256,string)",
}

/// Wrapper for the precompile function.
Expand Down Expand Up @@ -141,6 +145,45 @@ where
Err(err) => Err(revert_dispatch_error(err)),
}
},
Action::Evolve => {
let caller = context.caller;

input.expect_arguments(4)?;

let collection_id = input.read::<u64>()?;
let token_id = input.read::<TokenId>()?;
let token_uri_raw = input.read::<Bytes>()?.0;
let token_uri = token_uri_raw
.clone()
.try_into()
.map_err(|_| revert("invalid token uri length"))?;

match LaosEvolution::evolve_with_external_uri(
caller.into(),
collection_id,
token_id,
token_uri,
) {
Ok(()) => {
let mut token_id_bytes = [0u8; 32];
token_id.to_big_endian(&mut token_id_bytes);

LogsBuilder::new(context.address)
.log2(
SELECTOR_LOG_EVOLVED_WITH_EXTERNAL_TOKEN_URI,
token_id_bytes,
EvmDataWriter::new()
.write(collection_id)
.write(Bytes(token_uri_raw))
.build(),
)
.record(handle)?;

Ok(succeed(EvmDataWriter::new().write(token_id).build()))
},
Err(err) => Err(revert_dispatch_error(err)),
}
},
}
}
}
Expand All @@ -161,6 +204,7 @@ where
Action::Mint => FunctionModifier::NonPayable,
Action::OwnerOfCollection => FunctionModifier::View,
Action::TokenURI => FunctionModifier::View,
Action::Evolve => FunctionModifier::NonPayable,
})?;

Self::inner_execute(handle, &selector)
Expand Down
Loading