crowdloan.rs 46.7 KiB
Newer Older
	}

	#[test]
	fn onboard_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			assert_eq!(Balances::free_balance(1), 999);

			// Fund crowdloan
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, 2));

			run_to_block(10);

			// Cannot onboard invalid fund index
			assert_noop!(Crowdloan::onboard(Origin::signed(1), 1, 0.into()), Error::<Test>::InvalidFundIndex);
			// Cannot onboard crowdloan without deploy data
			assert_noop!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()), Error::<Test>::UnsetDeployData);

			// Add deploy data
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			));

			// Cannot onboard fund with incorrect parachain id
			assert_noop!(Crowdloan::onboard(Origin::signed(1), 0, 1.into()), SlotsError::<Test>::ParaNotOnboarding);
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));

			// Cannot onboard fund again
			assert_noop!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()), Error::<Test>::AlreadyOnboard);
		});
	}

	#[test]
	fn begin_retirement_works() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			assert_eq!(Balances::free_balance(1), 999);

			// Add deploy data
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			// Fund crowdloan
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, 2));
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));
			// Fund is assigned a parachain id
			let fund = Crowdloan::funds(0).unwrap();
			assert_eq!(fund.parachain, Some(0.into()));

			// Off-boarding is set to the crowdloan account
			assert_eq!(Slots::offboarding(ParaId::from(0)), Crowdloan::fund_account_id(0));
			// Retire crowdloan to remove parachain id
			assert_ok!(Crowdloan::begin_retirement(Origin::signed(1), 0));

			// Fund should no longer have parachain id
			let fund = Crowdloan::funds(0).unwrap();
			assert_eq!(fund.parachain, None);

		});
	}

	#[test]
	fn begin_retirement_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			assert_eq!(Balances::free_balance(1), 999);

			// Add deploy data
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			// Fund crowdloan
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 1000, 2));

			run_to_block(10);

			// Cannot retire fund that is not onboarded
			assert_noop!(Crowdloan::begin_retirement(Origin::signed(1), 0), Error::<Test>::NotParachain);
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));
			// Fund is assigned a parachain id
			let fund = Crowdloan::funds(0).unwrap();
			assert_eq!(fund.parachain, Some(0.into()));

			// Cannot retire fund whose deposit has not been returned
			assert_noop!(Crowdloan::begin_retirement(Origin::signed(1), 0), Error::<Test>::ParaHasDeposit);

			run_to_block(50);

			// Cannot retire invalid fund index
			assert_noop!(Crowdloan::begin_retirement(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);

			// Cannot retire twice
			assert_ok!(Crowdloan::begin_retirement(Origin::signed(1), 0));
			assert_noop!(Crowdloan::begin_retirement(Origin::signed(1), 0), Error::<Test>::NotParachain);
		});
	}

	#[test]
	fn withdraw_works() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Transfer fee is taken here
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, 1));
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, 2));
			assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, 3));

			// Skip all the way to the end
			run_to_block(50);

			// User can withdraw their full balance without fees
			assert_ok!(Crowdloan::withdraw(Origin::signed(1), 0));
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(1), 999);
			assert_ok!(Crowdloan::withdraw(Origin::signed(2), 0));
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(2), 2000);
			assert_ok!(Crowdloan::withdraw(Origin::signed(3), 0));
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(3), 3000);
	#[test]
	fn withdraw_other_works() {
		// User will contribute on behalf of another user
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Transfer fee is taken here
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, 4));
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, 4));
			assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, 4));

			// Skip all the way to the end
			run_to_block(50);

			// User can withdraw their full balance without fees
			assert_ok!(Crowdloan::withdraw(Origin::signed(4), 0));
			assert_eq!(Balances::free_balance(4), 4600);
		});
	}

	#[test]
	fn withdraw_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Transfer fee is taken here
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, 1));
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(1), 950);

			run_to_block(5);

			// Cannot withdraw before fund ends
			assert_noop!(Crowdloan::withdraw(Origin::signed(1), 0), Error::<Test>::FundNotEnded);

			run_to_block(10);

			// Cannot withdraw if they did not contribute
			assert_noop!(Crowdloan::withdraw(Origin::signed(2), 0), Error::<Test>::NoContributions);
			// Cannot withdraw from a non-existent fund
			assert_noop!(Crowdloan::withdraw(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
		});
	}

	#[test]
	fn dissolve_works() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Transfer fee is taken here
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, 1));
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, 2));
			assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, 3));

			// Skip all the way to the end
			run_to_block(50);
Gavin Wood's avatar
Gavin Wood committed
			// Check initiator's balance.
			assert_eq!(Balances::free_balance(1), 899);
			// Check current funds (contributions + deposit)
			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 601);
			// Dissolve the crowdloan
			assert_ok!(Crowdloan::dissolve(Origin::signed(1), 0));

			// Fund account is emptied
			assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(0)), 0);
			// Deposit is returned
Gavin Wood's avatar
Gavin Wood committed
			assert_eq!(Balances::free_balance(1), 900);
			// Treasury account is filled
			assert_eq!(Balances::free_balance(Treasury::account_id()), 600);

			// Storage trie is removed
			assert_eq!(Crowdloan::contribution_get(0,&0), 0);
			// Fund storage is removed
			assert_eq!(Crowdloan::funds(0), None);

		});
	}

	#[test]
	fn dissolve_handles_basic_errors() {
		new_test_ext().execute_with(|| {
			// Set up a crowdloan
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Transfer fee is taken here
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 100, 1));
			assert_ok!(Crowdloan::contribute(Origin::signed(2), 0, 200, 2));
			assert_ok!(Crowdloan::contribute(Origin::signed(3), 0, 300, 3));

			// Cannot dissolve an invalid fund index
			assert_noop!(Crowdloan::dissolve(Origin::signed(1), 1), Error::<Test>::InvalidFundIndex);
			// Cannot dissolve a fund in progress
			assert_noop!(Crowdloan::dissolve(Origin::signed(1), 0), Error::<Test>::InRetirementPeriod);

			run_to_block(10);

			// Onboard fund
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));

			// Cannot dissolve an active fund
			assert_noop!(Crowdloan::dissolve(Origin::signed(1), 0), Error::<Test>::HasActiveParachain);
		});
	}

	#[test]
	fn fund_before_auction_works() {
		new_test_ext().execute_with(|| {
			// Create a crowdloan before an auction is created
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 9));
			// Users can already contribute
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 49, 1));
			// Fund added to NewRaise
			assert_eq!(Crowdloan::new_raise(), vec![0]);

			// Some blocks later...
			run_to_block(2);
			// Create an auction
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			// Add deploy data
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			));
			// Move to the end of auction...
			run_to_block(12);

			// Endings count incremented
			assert_eq!(Crowdloan::endings_count(), 1);
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));
			let fund = Crowdloan::funds(0).unwrap();
			// Crowdloan is now assigned a parachain id
			assert_eq!(fund.parachain, Some(0.into()));
			// This parachain is managed by Slots
			assert_eq!(Slots::managed_ids(), vec![0.into()]);
		});
	}

	#[test]
	fn fund_across_multiple_auctions_works() {
		new_test_ext().execute_with(|| {
			// Create an auction
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			// Create two competing crowdloans, with end dates across multiple auctions
			// Each crowdloan is competing for the same slots, so only one can win
			assert_ok!(Crowdloan::create(Origin::signed(1), 1000, 1, 4, 30));
			assert_ok!(Crowdloan::create(Origin::signed(2), 1000, 1, 4, 30));

			// Contribute to all, but more money to 0, less to 1
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 0, 300, 1));
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 200, 1));

			// Add deploy data to all
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(1),
				0,
				<Test as frame_system::Config>::Hash::default(),
			assert_ok!(Crowdloan::fix_deploy_data(
				Origin::signed(2),
				1,
				<Test as frame_system::Config>::Hash::default(),
			));

			// End the current auction, fund 0 wins!
			run_to_block(10);
			assert_eq!(Crowdloan::endings_count(), 1);
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(1), 0, 0.into()));
			let fund = Crowdloan::funds(0).unwrap();
			// Crowdloan is now assigned a parachain id
			assert_eq!(fund.parachain, Some(0.into()));
			// This parachain is managed by Slots
			assert_eq!(Slots::managed_ids(), vec![0.into()]);

			// Create a second auction
			assert_ok!(Slots::new_auction(Origin::root(), 5, 1));
			// Contribute to existing funds add to NewRaise
			assert_ok!(Crowdloan::contribute(Origin::signed(1), 1, 10, 1));

			// End the current auction, fund 1 wins!
			run_to_block(20);
			assert_eq!(Crowdloan::endings_count(), 2);
			// Onboard crowdloan
			assert_ok!(Crowdloan::onboard(Origin::signed(2), 1, 1.into()));
			let fund = Crowdloan::funds(1).unwrap();
			// Crowdloan is now assigned a parachain id
			assert_eq!(fund.parachain, Some(1.into()));
			// This parachain is managed by Slots
			assert_eq!(Slots::managed_ids(), vec![0.into(), 1.into()]);
		});
	}
}