Skip to content

Commit

Permalink
feat: Add missing update_roles precompile function (#837)
Browse files Browse the repository at this point in the history
* feat: Add `update_roles` precompile function

* add tests

---------

Co-authored-by: 1xstj <[email protected]>
  • Loading branch information
yurixander and 1xstj authored Dec 3, 2024
1 parent 1ed2d9a commit 40603bb
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 12 deletions.
60 changes: 49 additions & 11 deletions precompiles/tangle-lst/TangleLst.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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);
}
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);
}
43 changes: 42 additions & 1 deletion precompiles/tangle-lst/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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::<Runtime>::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::<Runtime>::update_roles {
pool_id,
new_root,
new_nominator,
new_bouncer,
};
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;
Ok(())
}
}

impl<Runtime> TangleLstPrecompile<Runtime>
Expand Down
60 changes: 60 additions & 0 deletions precompiles/tangle-lst/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(());
});
}

0 comments on commit 40603bb

Please sign in to comment.