Skip to content

Commit

Permalink
Feat/split stake extrinsic #1699 (#1717)
Browse files Browse the repository at this point in the history
The goal of this PR is to split the `stake` extrinsic into two: `stake` and `provider_boost`
Closes #1707
  • Loading branch information
shannonwells committed Oct 18, 2023
1 parent f660087 commit ea9ccda
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 120 deletions.
28 changes: 27 additions & 1 deletion e2e/capacity/staking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
createKeys, createMsaAndProvider,
stakeToProvider,
getNextEpochBlock, TEST_EPOCH_LENGTH,
CENTS, DOLLARS, createAndFundKeypair
CENTS, DOLLARS, createAndFundKeypair, boostProvider
}
from "../scaffolding/helpers";
import { firstValueFrom } from "rxjs";
Expand Down Expand Up @@ -280,4 +280,30 @@ describe("Capacity Staking Tests", function () {
});
})
});

describe('provider_boost()', async () => {
let stakeKeys: KeyringPair;
let stakeProviderId: u64;
let capacityBoostMin: bigint = capacityMin/ 20n; // 5% of the amount

before(async function () {
stakeKeys = createKeys("StakeKeysProvider");
stakeProviderId = await createMsaAndProvider(stakeKeys, "SPBoost", accountBalance);
});

it('works, happy path', async () => {
await assert.doesNotReject(boostProvider(stakeKeys, stakeProviderId, tokenMinStake));

// Confirm that the tokens were locked in the stakeKeys account using the query API
const stakedAcctInfo = await ExtrinsicHelper.getAccountInfo(stakeKeys.address);
assert.equal(stakedAcctInfo.data.frozen, tokenMinStake, `expected ${tokenMinStake} frozen balance, got ${stakedAcctInfo.data.frozen}`)

// Confirm that the capacity was added to the stakeProviderId using the query API
const capacityStaked = (await firstValueFrom(ExtrinsicHelper.api.query.capacity.capacityLedger(stakeProviderId))).unwrap();
assert.equal(capacityStaked.remainingCapacity, capacityBoostMin, `expected capacityLedger.remainingCapacity = 1CENT, got ${capacityStaked.remainingCapacity}`);
assert.equal(capacityStaked.totalTokensStaked, tokenMinStake, `expected capacityLedger.totalTokensStaked = 1CENT, got ${capacityStaked.totalTokensStaked}`);
assert.equal(capacityStaked.totalCapacityIssued, capacityBoostMin, `expected capacityLedger.totalCapacityIssued = 1CENT, got ${capacityStaked.totalCapacityIssued}`);
trackedFrozenBalance += tokenMinStake;
})
})
})
2 changes: 1 addition & 1 deletion e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions e2e/scaffolding/extrinsicHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,8 +388,12 @@ export class ExtrinsicHelper {
public static setEpochLength(keys: KeyringPair, epoch_length: any): Extrinsic {
return new Extrinsic(() => ExtrinsicHelper.api.tx.capacity.setEpochLength(epoch_length), keys, ExtrinsicHelper.api.events.capacity.EpochLengthUpdated);
}
public static stake(keys: KeyringPair, target: any, amount: any, stakingType: 'MaximumCapacity' | 'ProviderBoost'): Extrinsic {
return new Extrinsic(() => ExtrinsicHelper.api.tx.capacity.stake(target, amount, stakingType), keys, ExtrinsicHelper.api.events.capacity.Staked);
public static stake(keys: KeyringPair, target: any, amount: any): Extrinsic {
return new Extrinsic(() => ExtrinsicHelper.api.tx.capacity.stake(target, amount), keys, ExtrinsicHelper.api.events.capacity.Staked);
}

public static providerBoost(keys: KeyringPair, target: any, amount: any): Extrinsic {
return new Extrinsic(() => ExtrinsicHelper.api.tx.capacity.providerBoost(target, amount), keys, ExtrinsicHelper.api.events.capacity.ProviderBoosted);
}

public static unstake(keys: KeyringPair, target: any, amount: any): Extrinsic {
Expand Down
17 changes: 17 additions & 0 deletions e2e/scaffolding/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,22 @@ export async function stakeToProvider(source: KeyringPair, keys: KeyringPair, pr
return Promise.reject('stakeToProvider: stakeEvent should be ExtrinsicHelper.api.events.capacity.Staked');
}
}
export async function boostProvider(keys: KeyringPair, providerId: u64, tokensToStake: bigint): Promise<void> {
const stakeOp = ExtrinsicHelper.providerBoost(keys, providerId, tokensToStake);
const [stakeEvent] = await stakeOp.fundAndSend();
assert.notEqual(stakeEvent, undefined, 'stakeToProvider: should have returned Stake event');

if (stakeEvent && ExtrinsicHelper.api.events.capacity.ProviderBoosted.is(stakeEvent)) {
let stakedCapacity = stakeEvent.data.capacity;

let expectedCapacity = tokensToStake/TokenPerCapacity/BoostAdjustment;

assert.equal(stakedCapacity, expectedCapacity, `stakeToProvider: expected ${expectedCapacity}, got ${stakedCapacity}`);
}
else {
return Promise.reject('stakeToProvider: stakeEvent should be ExtrinsicHelper.api.events.capacity.ProviderBoosted');
}
}

export async function getNextEpochBlock() {
const epochInfo = await firstValueFrom(ExtrinsicHelper.api.query.capacity.currentEpochInfo());
Expand Down Expand Up @@ -417,6 +433,7 @@ export async function getOrCreateAvroChatMessageItemizedSchema(source: KeyringPa
}

export const TokenPerCapacity = 50n;
export const BoostAdjustment = 20n; // divide by 20 or 5% of Maximum Capacity

export function assertEvent(events: EventMap, eventName: string) {
assert(events.hasOwnProperty(eventName));
Expand Down
19 changes: 17 additions & 2 deletions pallets/capacity/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,10 @@ benchmarks! {
let amount: BalanceOf<T> = T::MinimumStakingAmount::get();
let capacity: BalanceOf<T> = Capacity::<T>::capacity_generated(amount);
let target = 1;
let staking_type = StakingType::MaximumCapacity;

register_provider::<T>(target, "Foo");

}: _ (RawOrigin::Signed(caller.clone()), target, amount, staking_type)
}: _ (RawOrigin::Signed(caller.clone()), target, amount)
verify {
assert!(StakingAccountLedger::<T>::contains_key(&caller));
assert!(StakingTargetLedger::<T>::contains_key(&caller, target));
Expand Down Expand Up @@ -167,6 +166,22 @@ benchmarks! {
}.into());
}

provider_boost {
let caller: T::AccountId = create_funded_account::<T>("account", SEED, 105u32);
let amount: BalanceOf<T> = T::MinimumStakingAmount::get();
let capacity: BalanceOf<T> = Capacity::<T>::capacity_generated(amount);
let target = 1;

register_provider::<T>(target, "Foo");

}: _ (RawOrigin::Signed(caller.clone()), target, amount)
verify {
assert!(StakingAccountLedger::<T>::contains_key(&caller));
assert!(StakingTargetLedger::<T>::contains_key(&caller, target));
assert!(CapacityLedger::<T>::contains_key(target));
assert_last_event::<T>(Event::<T>::ProviderBoosted {account: caller, amount, target, capacity}.into());
}

impl_benchmark_test_suite!(Capacity,
crate::tests::mock::new_test_ext(),
crate::tests::mock::Test);
Expand Down
53 changes: 51 additions & 2 deletions pallets/capacity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ pub mod pallet {
#[pallet::event]
#[pallet::generate_deposit(pub (super) fn deposit_event)]
pub enum Event<T: Config> {
/// Tokens have been staked to the Frequency network.
/// Tokens have been staked to the Frequency network for Maximized Capacity.
Staked {
/// The token account that staked tokens to the network.
account: T::AccountId,
Expand Down Expand Up @@ -313,6 +313,17 @@ pub mod pallet {
/// The amount in token that was retargeted
amount: BalanceOf<T>,

Check warning on line 314 in pallets/capacity/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/capacity/src/lib.rs#L307-L314

Added lines #L307 - L314 were not covered by tests
},
/// Tokens have been staked on the network for Provider Boosting
ProviderBoosted {
/// The token account that staked tokens to the network.
account: T::AccountId,
/// The MSA that a token account targeted to receive Capacity based on this staking amount.
target: MessageSourceId,
/// An amount that was staked.
amount: BalanceOf<T>,
/// The Capacity amount issued to the target as a result of the stake.
capacity: BalanceOf<T>,

Check warning on line 325 in pallets/capacity/src/lib.rs

View check run for this annotation

Codecov / codecov/patch

pallets/capacity/src/lib.rs#L318-L325

Added lines #L318 - L325 were not covered by tests
},
}

#[pallet::error]
Expand Down Expand Up @@ -382,9 +393,9 @@ pub mod pallet {
origin: OriginFor<T>,
target: MessageSourceId,
amount: BalanceOf<T>,
staking_type: StakingType,
) -> DispatchResult {
let staker = ensure_signed(origin)?;
let staking_type = StakingType::MaximumCapacity;

let (mut staking_account, actual_amount) =
Self::ensure_can_stake(&staker, &target, &amount, &staking_type)?;
Expand Down Expand Up @@ -532,6 +543,44 @@ pub mod pallet {
});
Ok(())
}

/// Stakes some amount of tokens to the network and generates a comparatively small amount of Capacity
/// for the target, and gives periodic rewards to origin.
/// ### Errors
///
/// - Returns Error::InvalidTarget if attempting to stake to an invalid target.
/// - Returns Error::InsufficientStakingAmount if attempting to stake an amount below the minimum amount.
/// - Returns Error::CannotChangeStakingType if the staking account exists and staking_type is different
#[pallet::call_index(5)]
#[pallet::weight(T::WeightInfo::provider_boost())]
pub fn provider_boost(
origin: OriginFor<T>,
target: MessageSourceId,
amount: BalanceOf<T>,
) -> DispatchResult {
let staker = ensure_signed(origin)?;
let staking_type = StakingType::ProviderBoost;

let (mut staking_account, actual_amount) =
Self::ensure_can_stake(&staker, &target, &amount, &staking_type)?;

let capacity = Self::increase_stake_and_issue_capacity(
&staker,
&mut staking_account,
&target,
&actual_amount,
&staking_type,
)?;

Self::deposit_event(Event::ProviderBoosted {
account: staker,
amount: actual_amount,
target,
capacity,
});

Ok(())
}
}
}

Expand Down
24 changes: 4 additions & 20 deletions pallets/capacity/src/tests/change_staking_target_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,8 @@ fn check_retarget_multiple_stakers() {

setup_provider(&staker_10k, &from_msa, &647u64, ProviderBoost);
setup_provider(&staker_500, &to_msa, &293u64, ProviderBoost);
assert_ok!(Capacity::stake(
RuntimeOrigin::signed(staker_600.clone()),
from_msa,
479u64,
MaximumCapacity
));
assert_ok!(Capacity::stake(
RuntimeOrigin::signed(staker_400.clone()),
to_msa,
211u64,
MaximumCapacity
));
assert_ok!(Capacity::stake(RuntimeOrigin::signed(staker_600.clone()), from_msa, 479u64,));
assert_ok!(Capacity::stake(RuntimeOrigin::signed(staker_400.clone()), to_msa, 211u64,));

// 647 * .1 * .05 = 3 (rounded down)
// 293 * .1 * .05 = 1 (rounded down)
Expand Down Expand Up @@ -204,12 +194,7 @@ fn do_retarget_deletes_staking_target_details_if_zero_balance() {

// stake additional to provider from another Msa, doesn't matter which type.
// total staked to from_msa is now 22u64.
assert_ok!(Capacity::stake(
RuntimeOrigin::signed(300u64),
from_msa,
12u64,
MaximumCapacity
));
assert_ok!(Capacity::stake(RuntimeOrigin::signed(300u64), from_msa, 12u64,));

assert_ok!(Capacity::do_retarget(&staker, &from_msa, &to_msa, &amount, &MaximumCapacity));

Expand Down Expand Up @@ -362,11 +347,10 @@ fn change_staking_target_test_parametric_validity() {
setup_provider(&from_account, &from_target, &staked_amount, ProviderBoost);
setup_provider(&from_account, &to_target, &staked_amount, ProviderBoost);

assert_ok!(Capacity::stake(
assert_ok!(Capacity::provider_boost(
RuntimeOrigin::signed(from_account),
from_target,
staked_amount,
ProviderBoost
));

struct TestCase {
Expand Down
Loading

0 comments on commit ea9ccda

Please sign in to comment.