Search code examples
rustsubstrate

How to modify the Nicks Pallet (in Substrate) to set the nickname via a transfer of currency to another account, instead of using reserve currency?


In the Nicks Pallet, an account can set a nickname for itself via reserve currency. I would like to modify this functionality to instead require a fee to be paid to another specific account (for now I would just like to send the fee to the Alice account) in order to set a nickname.

This seems like it should be a fairly simply modification, but as I am new to Substrate and Rust this is not quite as straightforward as I would have thought. I've already forked the Nicks Pallet, but simply cannot quite figure out how to proceed from here.

I am working in a locally cloned version of substrate.


Solution

  • Here are the high level changes you would need to make:

    1. Introduce a new associated type to your pallet's configuration Trait:
    pub trait Trait: frame_system::Trait {
        // -- snip --
        // This is a new type that allows the runtime to configure where the payment should go.
        type PaymentReceiver: Get<Self::AccountId>;
    }
    
    1. Update set_name function to use a different Currency function. In this case we want to transfer instead of reserve:
    // New import needed
    use frame_support::traits::ExistenceRequirement::KeepAlive;
    
    fn set_name(origin, name: Vec<u8>) {
        let sender = ensure_signed(origin)?;
    
        ensure!(name.len() >= T::MinLength::get(), Error::<T>::TooShort);
        ensure!(name.len() <= T::MaxLength::get(), Error::<T>::TooLong);
    
        let deposit = if let Some((_, deposit)) = <NameOf<T>>::get(&sender) {
            Self::deposit_event(RawEvent::NameChanged(sender.clone()));
            deposit
        } else {
            let deposit = T::ReservationFee::get();
            // The only change is made here...
            T::Currency::transfer(&sender, &T::PaymentReceiver::get(), deposit.clone(), KeepAlive)?;
            Self::deposit_event(RawEvent::NameSet(sender.clone()));
            deposit
        };
    
        <NameOf<T>>::insert(&sender, (name, deposit));
    }
    
    1. Comment out the parts of the code where we would unreserve or slash_reserved, since these behaviors do not apply with this new logic.

    fn clear_name

    // let _ = T::Currency::unreserve(&sender, deposit.clone());
    

    fn kill_name

    // T::Slashed::on_unbalanced(T::Currency::slash_reserved(&target, deposit.clone()).0);
    
    1. Once you have made these changes, your pallet should compile just fine:
    substrate git:(master) ✗ cargo build -p pallet-nicks
       Compiling pallet-nicks v2.0.0-rc6 (/Users/shawntabrizi/Documents/GitHub/substrate/frame/nicks)
       Finished dev [unoptimized + debuginfo] target(s) in 2.48s
    
    1. To actually use these changes in your runtime, you will need to configure it with this new PaymentReceiver trait:
    // Here we define the value of the receiver
    // `//Alice` -> `5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY`
    // -> `0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d`
    // Using: https://www.shawntabrizi.com/substrate-js-utilities/
    
    ord_parameter_types! {
        pub const PaymentReceiverValue: AccountId = AccountId::from(
            hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")
        );
    }
    

    You may need to introduce the hex_literal crate into your runtime.

    And we use this value in our trait implementation:

    impl pallet_nicks::Trait for Runtime {
        // -- snip --
        type PaymentReceiver: PaymentReceiverValue;
    }
    

    And that is it! Let me know if this helps.