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 Staking precompile delegation amount getter #2005

Merged
merged 9 commits into from
Jan 4, 2023
20 changes: 20 additions & 0 deletions precompiles/parachain-staking/StakingInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ interface ParachainStaking {
/// @return The total points awarded to all collators in the round
function points(uint256 round) external view returns (uint256);

/// @dev The amount delegated in support of the candidate by the delegator
/// @custom:selector a73e51bc
/// @param delegator Who made this delegation
/// @param candidate The candidate for which the delegation is in support of
/// @return The amount of the delegation in support of the candidate by the delegator
function delegationAmount(address delegator, address candidate)
external
view
returns (uint256);

/// @dev Whether the delegation is in the top delegations
/// @custom:selector 91cc8657
/// @param delegator Who made this delegation
/// @param candidate The candidate for which the delegation is in support of
/// @return If delegation is in top delegations (is counted)
function isInTopDelegations(address delegator, address candidate)
external
view
returns (bool);

/// @dev Get the minimum delegation amount
/// @custom:selector 02985992
/// @return The minimum delegation amount
Expand Down
56 changes: 56 additions & 0 deletions precompiles/parachain-staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,63 @@ where
Ok(selected_candidates)
}

#[precompile::public("delegationAmount(address,address)")]
#[precompile::view]
fn delegation_amount(
handle: &mut impl PrecompileHandle,
delegator: Address,
candidate: Address,
) -> EvmResult<U256> {
// Fetch info.
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
let (candidate, delegator) = (
Runtime::AddressMapping::into_account_id(candidate.0),
Runtime::AddressMapping::into_account_id(delegator.0),
);
let amount = pallet_parachain_staking::Pallet::<Runtime>::delegator_state(&delegator)
.and_then(|state| {
state
.delegations
.0
.into_iter()
.find(|b| b.owner == candidate)
})
.map_or(
U256::zero(),
|pallet_parachain_staking::Bond { amount, .. }| amount.into(),
);

Ok(amount)
}

// Role Verifiers
#[precompile::public("isInTopDelegations(address,address)")]
#[precompile::view]
fn is_in_top_delegations(
handle: &mut impl PrecompileHandle,
delegator: Address,
candidate: Address,
) -> EvmResult<bool> {
let (candidate, delegator) = (
Runtime::AddressMapping::into_account_id(candidate.0),
Runtime::AddressMapping::into_account_id(delegator.0),
);

// Fetch info.
handle.record_cost(RuntimeHelper::<Runtime>::db_read_gas_cost())?;
let is_in_top_delegations = pallet_parachain_staking::Pallet::<Runtime>::top_delegations(
&candidate,
)
.map_or(false, |delegations| {
delegations
.delegations
.into_iter()
.any(|b| b.owner == delegator)
});

Ok(is_in_top_delegations)
}

#[precompile::public("isDelegator(address)")]
#[precompile::public("is_delegator(address)")]
#[precompile::view]
Expand Down
2 changes: 1 addition & 1 deletion precompiles/parachain-staking/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ parameter_types! {
pub const DelegationBondLessDelay: u32 = 2;
pub const RewardPaymentDelay: u32 = 2;
pub const MinSelectedCandidates: u32 = 5;
pub const MaxTopDelegationsPerCandidate: u32 = 4;
pub const MaxTopDelegationsPerCandidate: u32 = 2;
pub const MaxBottomDelegationsPerCandidate: u32 = 4;
pub const MaxDelegationsPerDelegator: u32 = 4;
pub const MinCollatorStk: u128 = 10;
Expand Down
124 changes: 124 additions & 0 deletions precompiles/parachain-staking/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ fn selectors() {
assert!(PCall::is_delegator_selectors().contains(&0xfd8ab482));
assert!(PCall::is_candidate_selectors().contains(&0xd51b9e93));
assert!(PCall::is_selected_candidate_selectors().contains(&0x740d7d2a));
assert!(PCall::delegation_amount_selectors().contains(&0xa73e51bc));
assert!(PCall::is_in_top_delegations_selectors().contains(&0x91cc8657));
assert!(PCall::points_selectors().contains(&0x9799b4e7));
assert!(PCall::min_delegation_selectors().contains(&0x02985992));
assert!(PCall::candidate_count_selectors().contains(&0xa9a981a3));
Expand Down Expand Up @@ -89,6 +91,8 @@ fn modifiers() {
tester.test_view_modifier(PCall::is_candidate_selectors());
tester.test_view_modifier(PCall::is_selected_candidate_selectors());
tester.test_view_modifier(PCall::points_selectors());
tester.test_view_modifier(PCall::delegation_amount_selectors());
tester.test_view_modifier(PCall::is_in_top_delegations_selectors());
tester.test_view_modifier(PCall::min_delegation_selectors());
tester.test_view_modifier(PCall::candidate_count_selectors());
tester.test_view_modifier(PCall::round_selectors());
Expand Down Expand Up @@ -183,6 +187,126 @@ fn points_non_zero() {
});
}

#[test]
fn delegation_amount_zero() {
ExtBuilder::default()
.with_balances(vec![(Alice.into(), 1_000)])
.build()
.execute_with(|| {
precompiles()
.prepare_test(
Alice,
Precompile1,
PCall::delegation_amount {
delegator: Address(Alice.into()),
candidate: Address(Alice.into()),
},
)
.expect_cost(0) // TODO: Test db read/write costs
.expect_no_logs()
.execute_returns_encoded(0u32);
});
}

#[test]
fn delegation_amount_nonzero() {
ExtBuilder::default()
.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 1_000)])
.with_candidates(vec![(Alice.into(), 1_000)])
.with_delegations(vec![(Bob.into(), Alice.into(), 1_000)])
.build()
.execute_with(|| {
precompiles()
.prepare_test(
Alice,
Precompile1,
PCall::delegation_amount {
delegator: Address(Bob.into()),
candidate: Address(Alice.into()),
},
)
.expect_cost(0) // TODO: Test db read/write costs
4meta5 marked this conversation as resolved.
Show resolved Hide resolved
.expect_no_logs()
.execute_returns_encoded(1000u32);
});
}

#[test]
fn is_not_in_top_delegations_when_delegation_dne() {
ExtBuilder::default()
.with_balances(vec![(Alice.into(), 1_000)])
.build()
.execute_with(|| {
precompiles()
.prepare_test(
Alice,
Precompile1,
PCall::delegation_amount {
delegator: Address(Alice.into()),
candidate: Address(Alice.into()),
},
)
.expect_cost(0) // TODO: Test db read/write costs
.expect_no_logs()
.execute_returns_encoded(false);
});
}

#[test]
fn is_not_in_top_delegations_because_not_in_top() {
ExtBuilder::default()
.with_balances(vec![
(Alice.into(), 1_000),
(Bob.into(), 500),
(Charlie.into(), 501),
(David.into(), 502),
])
.with_candidates(vec![(Alice.into(), 1_000)])
.with_delegations(vec![
(Bob.into(), Alice.into(), 500),
(Charlie.into(), Alice.into(), 501),
(David.into(), Alice.into(), 502),
])
.build()
.execute_with(|| {
precompiles()
.prepare_test(
Alice,
Precompile1,
PCall::is_in_top_delegations {
delegator: Address(Bob.into()),
candidate: Address(Alice.into()),
},
)
.expect_cost(0) // TODO: Test db read/write costs
.expect_no_logs()
.execute_returns_encoded(false);
});
}

#[test]
fn is_in_top_delegations() {
ExtBuilder::default()
.with_balances(vec![(Alice.into(), 1_000), (Bob.into(), 500)])
.with_candidates(vec![(Alice.into(), 1_000)])
.with_delegations(vec![(Bob.into(), Alice.into(), 500)])
.build()
.execute_with(|| {
precompiles()
.prepare_test(
Alice,
Precompile1,
PCall::is_in_top_delegations {
delegator: Address(Bob.into()),
candidate: Address(Alice.into()),
},
)
.expect_cost(0) // TODO: Test db read/write costs
.expect_no_logs()
.execute_returns_encoded(true);
});
}

#[test]
fn round_works() {
ExtBuilder::default().build().execute_with(|| {
Expand Down