diff --git a/precompiles/tangle-lst/TangleLst.sol b/precompiles/tangle-lst/TangleLst.sol index 737e14a4..0509880d 100644 --- a/precompiles/tangle-lst/TangleLst.sol +++ b/precompiles/tangle-lst/TangleLst.sol @@ -13,24 +13,39 @@ interface TangleLst { /// @param poolId The ID of the pool. /// @param extraType The type of extra bond (0 for FreeBalance, 1 for Rewards). /// @param extra The amount of extra bond. - function bondExtra(uint256 poolId, uint8 extraType, uint256 extra) external returns (uint8); + function bondExtra( + uint256 poolId, + uint8 extraType, + uint256 extra + ) external returns (uint8); /// @dev Unbond from a pool. /// @param memberAccount The account of the member. /// @param poolId The ID of the pool. /// @param unbondingPoints The amount of unbonding points. - function unbond(bytes32 memberAccount, uint256 poolId, uint256 unbondingPoints) external returns (uint8); + function unbond( + bytes32 memberAccount, + uint256 poolId, + uint256 unbondingPoints + ) external returns (uint8); /// @dev Withdraw unbonded funds from a pool. /// @param poolId The ID of the pool. /// @param numSlashingSpans The number of slashing spans. - function poolWithdrawUnbonded(uint256 poolId, uint32 numSlashingSpans) external returns (uint8); + function poolWithdrawUnbonded( + uint256 poolId, + uint32 numSlashingSpans + ) external returns (uint8); /// @dev Withdraw unbonded funds for a member. /// @param memberAccount The account of the member. /// @param poolId The ID of the pool. /// @param numSlashingSpans The number of slashing spans. - function withdrawUnbonded(bytes32 memberAccount, uint256 poolId, uint32 numSlashingSpans) external returns (uint8); + function withdrawUnbonded( + bytes32 memberAccount, + uint256 poolId, + uint32 numSlashingSpans + ) external returns (uint8); /// @dev Create a new pool. /// @param amount The initial amount to create the pool with. @@ -40,9 +55,9 @@ interface TangleLst { /// @param name The name of the pool. /// @param icon The icon of the pool. function create( - uint256 amount, - bytes32 root, - bytes32 nominator, + uint256 amount, + bytes32 root, + bytes32 nominator, bytes32 bouncer, bytes calldata name, bytes calldata icon @@ -51,7 +66,10 @@ interface TangleLst { /// @dev Nominate validators for a pool. /// @param poolId The ID of the pool. /// @param validators An array of validator accounts to nominate. - function nominate(uint256 poolId, bytes32[] calldata validators) external returns (uint8); + function nominate( + uint256 poolId, + bytes32[] calldata validators + ) external returns (uint8); /// @dev Set the state of a pool. /// @param poolId The ID of the pool. @@ -61,12 +79,32 @@ interface TangleLst { /// @dev Set metadata for a pool. /// @param poolId The ID of the pool. /// @param metadata The metadata to set. - function setMetadata(uint256 poolId, bytes calldata metadata) external returns (uint8); + function setMetadata( + uint256 poolId, + bytes calldata metadata + ) external returns (uint8); /// @dev Set global configurations (only callable by root). /// @param minJoinBond The minimum bond required to join a pool (0 for no change). /// @param minCreateBond The minimum bond required to create a pool (0 for no change). /// @param maxPools The maximum number of pools (0 for no change). /// @param globalMaxCommission The global maximum commission percentage (0 for no change). - function setConfigs(uint256 minJoinBond, uint256 minCreateBond, uint32 maxPools, uint32 globalMaxCommission) external returns (uint8); -} \ No newline at end of file + function setConfigs( + uint256 minJoinBond, + uint256 minCreateBond, + uint32 maxPools, + uint32 globalMaxCommission + ) external returns (uint8); + + /// @dev Update roles for a pool. + /// @param poolId The ID of the pool. + /// @param root The new root account. + /// @param nominator The new nominator account. + /// @param bouncer The new bouncer account. + function updateRoles( + uint256 poolId, + bytes32 root, + bytes32 nominator, + bytes32 bouncer + ) external returns (uint8); +} diff --git a/precompiles/tangle-lst/src/lib.rs b/precompiles/tangle-lst/src/lib.rs index 0fdd0681..de005e09 100644 --- a/precompiles/tangle-lst/src/lib.rs +++ b/precompiles/tangle-lst/src/lib.rs @@ -41,7 +41,7 @@ use frame_support::{ traits::Currency, }; use pallet_evm::AddressMapping; -use pallet_tangle_lst::{BondExtra, PoolId, PoolState}; +use pallet_tangle_lst::{BondExtra, ConfigOp, PoolId, PoolState}; use precompile_utils::prelude::*; use sp_core::{H160, H256, U256}; use sp_runtime::traits::Dispatchable; @@ -279,6 +279,47 @@ where Ok(()) } + + #[precompile::public("updateRoles(uint256,bytes32,bytes32,bytes32)")] + fn update_roles( + handle: &mut impl PrecompileHandle, + pool_id: U256, + new_root: H256, + new_nominator: H256, + new_bouncer: H256, + ) -> EvmResult { + handle.record_cost(RuntimeHelper::::db_read_gas_cost())?; + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + + let pool_id: PoolId = pool_id.try_into().map_err(|_| revert("Invalid pool id"))?; + + let new_root = if new_root == H256::zero() { + ConfigOp::Noop + } else { + ConfigOp::Set(Self::convert_to_account_id(new_root)?) + }; + + let new_nominator = if new_nominator == H256::zero() { + ConfigOp::Noop + } else { + ConfigOp::Set(Self::convert_to_account_id(new_nominator)?) + }; + + let new_bouncer = if new_bouncer == H256::zero() { + ConfigOp::Noop + } else { + ConfigOp::Set(Self::convert_to_account_id(new_bouncer)?) + }; + + let call = pallet_tangle_lst::Call::::update_roles { + pool_id, + new_root, + new_nominator, + new_bouncer, + }; + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + Ok(()) + } } impl TangleLstPrecompile diff --git a/precompiles/tangle-lst/src/tests.rs b/precompiles/tangle-lst/src/tests.rs index f3d718b2..190d8dca 100644 --- a/precompiles/tangle-lst/src/tests.rs +++ b/precompiles/tangle-lst/src/tests.rs @@ -171,3 +171,63 @@ fn test_nominate() { .execute_returns(()); }); } + +#[test] +fn test_update_roles() { + ExtBuilder::default().build().execute_with(|| { + // First create a pool + let root = sp_core::sr25519::Public::from(TestAccount::Bob).into(); + let nominator = sp_core::sr25519::Public::from(TestAccount::Charlie).into(); + let bouncer = sp_core::sr25519::Public::from(TestAccount::Dave).into(); + let name = b"Test Pool".to_vec(); + let icon = b"icon_data".to_vec(); + + PrecompilesValue::get() + .prepare_test( + TestAccount::Alex, + H160::from_low_u64_be(1), + PCall::create { + amount: U256::from(10_000), + root, + nominator, + bouncer, + name: name.clone(), + icon: icon.clone(), + }, + ) + .execute_returns(()); + + // New roles + let new_root = sp_core::sr25519::Public::from(TestAccount::Dave).into(); + let new_nominator = sp_core::sr25519::Public::from(TestAccount::Eve).into(); + let new_bouncer = sp_core::sr25519::Public::from(TestAccount::Charlie).into(); + + // Try to update roles from non-root account (should fail) + PrecompilesValue::get() + .prepare_test( + TestAccount::Dave, // Using nominator account + H160::from_low_u64_be(1), + PCall::update_roles { + pool_id: U256::from(1), + new_root, + new_nominator, + new_bouncer, + }, + ) + .execute_reverts(|output| output == b"Dispatched call failed with error: Module(ModuleError { index: 5, error: [16, 0, 0, 0], message: Some(\"DoesNotHavePermission\") })"); + + // Update roles from root account (should succeed) + PrecompilesValue::get() + .prepare_test( + TestAccount::Bob, // Using root account + H160::from_low_u64_be(1), + PCall::update_roles { + pool_id: U256::from(1), + new_root, + new_nominator, + new_bouncer, + }, + ) + .execute_returns(()); + }); +}