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

Add owner admin frezer issuer accessors to assets precompile #2012

Merged
merged 8 commits into from
Dec 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions precompiles/assets-erc20/ERC20.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;

/// @author The Moonbeam Team
/// @title ERC20 interface
/// @dev see https://github.com/ethereum/EIPs/issues/20
/// @dev copied from https://github.com/OpenZeppelin/openzeppelin-contracts
Expand Down
1 change: 1 addition & 0 deletions precompiles/assets-erc20/Permit.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;

/// @author The Moonbeam Team
/// @title Extension of the ERC20 interface that allows users to
/// sign permit messages to interact with contracts without needing to
/// make a first approve transaction.
Expand Down
43 changes: 43 additions & 0 deletions precompiles/assets-erc20/Roles.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;

/// @author The Moonbeam Team
/// @title ERC20 interface Asset Roles
/// @dev Extension of the ERC20 interface that allows users to get the account capable of fulfilling different asset roles
/// @custom:address 0xFFFFFFFE + hex(assetId)
interface Roles {
/// @dev Function to check the owner of the asset
/// @custom:selector 8da5cb5b
/// @return the address of the owner.
function owner()
external
view
returns (address);

/// @dev Function to check the freezer of the asset
/// @dev Freezer: the account that can freeze an asset
/// @custom:selector 92716054
/// @return the address of the freezer.
function freezer()
external
view
returns (address);

/// @dev Function to check the issuer of the asset
/// @dev Issuer: the account that can issue tokens for an asset
/// @custom:selector 1d143848
/// @return the address of the issuer.
function issuer()
external
view
returns (address);

/// @dev Function to check the admin of the asset
/// @dev Admin: the account that can unfreeze and force transfer
/// @custom:selector f851a440
/// @return the address of the admin.
function admin()
external
view
returns (address);
}
1 change: 1 addition & 0 deletions precompiles/assets-erc20/src/eip2612.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ where
IsLocal: Get<bool>,
<Runtime as pallet_timestamp::Config>::Moment: Into<U256>,
AssetIdOf<Runtime, Instance>: Display,
Runtime::AccountId: Into<H160>,
{
fn compute_domain_separator(address: H160, asset_id: AssetIdOf<Runtime, Instance>) -> [u8; 32] {
let asset_name = pallet_assets::Pallet::<Runtime, Instance>::name(asset_id);
Expand Down
67 changes: 65 additions & 2 deletions precompiles/assets-erc20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

use core::fmt::Display;
use fp_evm::PrecompileHandle;
use frame_support::traits::fungibles::approvals::Inspect as ApprovalInspect;
use frame_support::traits::fungibles::metadata::Inspect as MetadataInspect;
use frame_support::traits::fungibles::Inspect;
use frame_support::traits::fungibles::{
approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect,
roles::Inspect as RolesInspect,
};
use frame_support::traits::{ConstBool, Get, OriginTrait};
use frame_support::{
dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo},
Expand Down Expand Up @@ -130,6 +132,7 @@ where
IsLocal: Get<bool>,
<Runtime as pallet_timestamp::Config>::Moment: Into<U256>,
AssetIdOf<Runtime, Instance>: Display,
Runtime::AccountId: Into<H160>,
{
/// PrecompileSet discrimiant. Allows to knows if the address maps to an asset id,
/// and if this is the case which one.
Expand Down Expand Up @@ -419,6 +422,66 @@ where
))
}

#[precompile::public("owner()")]
#[precompile::view]
fn owner(
asset_id: AssetIdOf<Runtime, Instance>,
handle: &mut impl PrecompileHandle,
) -> EvmResult<Address> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let owner: H160 = pallet_assets::Pallet::<Runtime, Instance>::owner(asset_id)
.ok_or(revert("No owner set"))?
.into();

Ok(Address(owner))
}

#[precompile::public("issuer()")]
#[precompile::view]
fn issuer(
asset_id: AssetIdOf<Runtime, Instance>,
handle: &mut impl PrecompileHandle,
) -> EvmResult<Address> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let issuer: H160 = pallet_assets::Pallet::<Runtime, Instance>::issuer(asset_id)
.ok_or(revert("No issuer set"))?
.into();

Ok(Address(issuer))
}

#[precompile::public("admin()")]
#[precompile::view]
fn admin(
asset_id: AssetIdOf<Runtime, Instance>,
handle: &mut impl PrecompileHandle,
) -> EvmResult<Address> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let admin: H160 = pallet_assets::Pallet::<Runtime, Instance>::admin(asset_id)
.ok_or(revert("No admin set"))?
.into();

Ok(Address(admin))
}

#[precompile::public("freezer()")]
#[precompile::view]
fn freezer(
asset_id: AssetIdOf<Runtime, Instance>,
handle: &mut impl PrecompileHandle,
) -> EvmResult<Address> {
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;

let freezer: H160 = pallet_assets::Pallet::<Runtime, Instance>::freezer(asset_id)
.ok_or(revert("No freezer set"))?
.into();

Ok(Address(freezer))
}

// From here: only for locals, we need to check whether we are in local assets otherwise fail
#[precompile::public("mint(address,uint256)")]
fn mint(
Expand Down
132 changes: 132 additions & 0 deletions precompiles/assets-erc20/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ fn selectors() {
assert!(ForeignPCall::total_supply_selectors().contains(&0x18160ddd));
assert!(ForeignPCall::approve_selectors().contains(&0x095ea7b3));
assert!(ForeignPCall::allowance_selectors().contains(&0xdd62ed3e));
assert!(ForeignPCall::freezer_selectors().contains(&0x92716054));
assert!(ForeignPCall::owner_selectors().contains(&0x8da5cb5b));
assert!(ForeignPCall::issuer_selectors().contains(&0x1d143848));
assert!(ForeignPCall::admin_selectors().contains(&0xf851a440));
assert!(ForeignPCall::transfer_selectors().contains(&0xa9059cbb));
assert!(ForeignPCall::transfer_from_selectors().contains(&0x23b872dd));
assert!(ForeignPCall::name_selectors().contains(&0x06fdde03));
Expand Down Expand Up @@ -2423,6 +2427,134 @@ fn burn_overflow() {
});
}

#[test]
fn get_owner() {
ExtBuilder::default()
.with_balances(vec![(CryptoAlith.into(), 1000), (Bob.into(), 2500)])
.build()
.execute_with(|| {
assert_ok!(LocalAssets::force_create(
RuntimeOrigin::root(),
0u128,
CryptoAlith.into(),
true,
1
));

assert_ok!(LocalAssets::transfer_ownership(
RuntimeOrigin::signed(CryptoAlith.into()),
0u128,
// owner
Bob.into(),
));

precompiles()
.prepare_test(CryptoAlith, LocalAssetId(0u128), ForeignPCall::owner {})
.expect_cost(0)
.expect_no_logs()
.execute_returns_encoded(Address(Bob.into()));
});
}

#[test]
fn get_issuer() {
ExtBuilder::default()
.with_balances(vec![(CryptoAlith.into(), 1000), (Bob.into(), 2500)])
.build()
.execute_with(|| {
assert_ok!(LocalAssets::force_create(
RuntimeOrigin::root(),
0u128,
CryptoAlith.into(),
true,
1
));

assert_ok!(LocalAssets::set_team(
RuntimeOrigin::signed(CryptoAlith.into()),
0u128,
// Issuer
Bob.into(),
// admin
CryptoAlith.into(),
// freezer
CryptoAlith.into(),
));

precompiles()
.prepare_test(CryptoAlith, LocalAssetId(0u128), ForeignPCall::issuer {})
.expect_cost(0)
.expect_no_logs()
.execute_returns_encoded(Address(Bob.into()));
});
}

#[test]
fn get_admin() {
ExtBuilder::default()
.with_balances(vec![(CryptoAlith.into(), 1000), (Bob.into(), 2500)])
.build()
.execute_with(|| {
assert_ok!(LocalAssets::force_create(
RuntimeOrigin::root(),
0u128,
CryptoAlith.into(),
true,
1
));

assert_ok!(LocalAssets::set_team(
RuntimeOrigin::signed(CryptoAlith.into()),
0u128,
// Issuer
CryptoAlith.into(),
// admin
Bob.into(),
// freezer
CryptoAlith.into(),
));

precompiles()
.prepare_test(CryptoAlith, LocalAssetId(0u128), ForeignPCall::admin {})
.expect_cost(0)
.expect_no_logs()
.execute_returns_encoded(Address(Bob.into()));
});
}

#[test]
fn get_freezer() {
ExtBuilder::default()
.with_balances(vec![(CryptoAlith.into(), 1000), (Bob.into(), 2500)])
.build()
.execute_with(|| {
assert_ok!(LocalAssets::force_create(
RuntimeOrigin::root(),
0u128,
CryptoAlith.into(),
true,
1
));

assert_ok!(LocalAssets::set_team(
RuntimeOrigin::signed(CryptoAlith.into()),
0u128,
// Issuer
CryptoAlith.into(),
// admin
CryptoAlith.into(),
// freezer
Bob.into(),
));

precompiles()
.prepare_test(CryptoAlith, LocalAssetId(0u128), ForeignPCall::freezer {})
.expect_cost(0)
.expect_no_logs()
.execute_returns_encoded(Address(Bob.into()));
});
}

#[test]
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
for file in ["ERC20.sol", "LocalAsset.sol", "Permit.sol"] {
Expand Down