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

feat: api fungibles pallet #113

Merged
merged 22 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions Cargo.lock

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

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ members = [
"runtime/devnet",
"runtime/testnet",
"integration-tests",
"pallets/*",
"primitives",
"scripts/fund-dev-accounts",
]
Expand Down Expand Up @@ -51,10 +52,11 @@ substrate-wasm-builder = "18.0.1"
substrate-build-script-utils = "11.0.0"

# Local
pallet-api = { path = "pallets/api", default-features = false }
pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds
pop-runtime-common = { path = "runtime/common", default-features = false }
pop-primitives = { path = "./primitives", default-features = false }
primitives = { path = "./primitives", default-features = false }

# Substrate
sc-basic-authorship = "0.35.0"
Expand Down
52 changes: 52 additions & 0 deletions pallets/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[package]
name = "pallet-api"
authors.workspace = true
description = "Api pallet, enabling smart(er) contracts with the power of Polkadot"
edition.workspace = true
license.workspace = true
version = "0.1.0"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec.workspace = true
scale-info.workspace = true

# Substrate
frame-benchmarking.workspace = true
frame-support.workspace = true
frame-system.workspace = true
pallet-assets.workspace = true
primitives = { workspace = true, features = ["fungibles"] }
sp-runtime.workspace = true

[dev-dependencies]
sp-core.workspace = true
sp-io.workspace = true

[features]
default = ["std"]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
std = [
"codec/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"pallet-assets/std",
"primitives/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
]
try-runtime = [
"frame-support/try-runtime",
"frame-system/try-runtime",
"sp-runtime/try-runtime",
]
122 changes: 122 additions & 0 deletions pallets/api/src/fungibles/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub use pallet::*;

#[cfg(test)]
mod tests;

#[frame_support::pallet]
pub mod pallet {
use frame_support::{
dispatch::{DispatchResult, DispatchResultWithPostInfo, WithPostDispatchInfo},
pallet_prelude::*,
traits::fungibles::{
approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect,
},
};
use frame_system::pallet_prelude::*;
use pallet_assets::WeightInfo;
use sp_runtime::traits::StaticLookup;

use primitives::constants::fungibles::*;

type AccountIdOf<T> = <T as frame_system::Config>::AccountId;
type AssetIdOf<T> = <pallet_assets::Pallet<T, AssetsInstanceOf<T>> as Inspect<
<T as frame_system::Config>::AccountId,
>>::AssetId;
type AssetIdParameterOf<T> =
<T as pallet_assets::Config<AssetsInstanceOf<T>>>::AssetIdParameter;
type Assets<T> = pallet_assets::Pallet<T, AssetsInstanceOf<T>>;
type AssetsInstanceOf<T> = <T as Config>::AssetsInstance;
type AssetsWeightInfo<T> = <T as pallet_assets::Config<AssetsInstanceOf<T>>>::WeightInfo;
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
type BalanceOf<T> = <pallet_assets::Pallet<T, AssetsInstanceOf<T>> as Inspect<
<T as frame_system::Config>::AccountId,
>>::Balance;

/// The required input for state queries in pallet assets.
#[derive(Encode, Decode, Debug, MaxEncodedLen)]
#[repr(u8)]
#[allow(clippy::unnecessary_cast)]
pub enum Keys<T: Config> {
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
TotalSupply(AssetIdOf<T>) = TOTAL_SUPPLY,
BalanceOf(AssetIdOf<T>, AccountIdOf<T>) = BALANCE_OF,
Allowance(AssetIdOf<T>, AccountIdOf<T>, AccountIdOf<T>) = ALLOWANCE,
TokenName(AssetIdOf<T>) = TOKEN_NAME,
TokenSymbol(AssetIdOf<T>) = TOKEN_SYMBOL,
TokenDecimals(AssetIdOf<T>) = TOKEN_DECIMALS,
}

#[pallet::config]
pub trait Config: frame_system::Config + pallet_assets::Config<Self::AssetsInstance> {
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
type AssetsInstance;
}

#[pallet::pallet]
pub struct Pallet<T>(_);

#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::call_index(9)]
#[pallet::weight(AssetsWeightInfo::<T>::transfer_keep_alive())]
pub fn transfer(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important question: what happens when v1 needed. Is the plan to create a whole new pallet, taking up an index in the runtime, or to just add new functions here?

If the latter, should these functions be prefixed with v0_ to indicate intentions? Whilst renaming wont affect encoding based on call index, it may break integrations using the name obtained via metadata.

Copy link
Collaborator Author

@Daanvdplas Daanvdplas Jul 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to predict but this theoretically doesn't have to change, ever. As long as the standards are being used the api in the contract should never change. If parameter types change slightly, these should be handled in the pallet via a runtime upgrade but this won't require a different version because the type in the contract stays the same.

When a parameter gets added to pallet assets' transfer there is a list of things that will happen:

  • For old contracts nothing will change, the pallet fungibles' transfer will add some logic via a runtime upgrade.

should these functions be prefixed with v0_ to indicate intentions? Whilst renaming wont affect encoding based on call index, it may break integrations using the name obtained via metadata.

Renaming: makes sure integrations outside of contracts won't break.
Not renaming: makes the code cleaner and there is a high chance we will never need it. If we need it in the end we can change the naming and integrations outside of contracts will break.

  • New contracts can use a v1::fungibles::transfer where they provide the extra parameter. This function will have a different dispatchable index and will call the v1_transfer()

origin: OriginFor<T>,
id: AssetIdOf<T>,
target: AccountIdOf<T>,
amount: BalanceOf<T>,
) -> DispatchResult {
let target = T::Lookup::unlookup(target);
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
Assets::<T>::transfer_keep_alive(origin, id.into(), target, amount)
}

#[pallet::call_index(10)]
#[pallet::weight(AssetsWeightInfo::<T>::cancel_approval() + AssetsWeightInfo::<T>::approve_transfer())]
pub fn approve(
origin: OriginFor<T>,
id: AssetIdOf<T>,
spender: AccountIdOf<T>,
value: BalanceOf<T>,
) -> DispatchResultWithPostInfo {
let spender = T::Lookup::unlookup(spender);
let id: AssetIdParameterOf<T> = id.into();
Assets::<T>::cancel_approval(origin.clone(), id.clone(), spender.clone())
.map_err(|e| e.with_weight(AssetsWeightInfo::<T>::cancel_approval()))?;
Assets::<T>::approve_transfer(origin, id, spender, value).map_err(|e| {
e.with_weight(
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
AssetsWeightInfo::<T>::cancel_approval()
+ AssetsWeightInfo::<T>::approve_transfer(),
)
})?;
Ok(().into())
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl<T: Config> Pallet<T> {
pub fn total_supply(id: AssetIdOf<T>) -> BalanceOf<T> {
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
Assets::<T>::total_supply(id)
}

pub fn balance_of(id: AssetIdOf<T>, owner: &AccountIdOf<T>) -> BalanceOf<T> {
Assets::<T>::balance(id, owner)
}

pub fn allowance(
id: AssetIdOf<T>,
owner: &AccountIdOf<T>,
spender: &AccountIdOf<T>,
) -> BalanceOf<T> {
Assets::<T>::allowance(id, owner, spender)
}

pub fn token_name(id: AssetIdOf<T>) -> Vec<u8> {
<Assets<T> as MetadataInspect<AccountIdOf<T>>>::name(id)
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn token_symbol(id: AssetIdOf<T>) -> Vec<u8> {
<Assets<T> as MetadataInspect<AccountIdOf<T>>>::symbol(id)
}

pub fn token_decimals(id: AssetIdOf<T>) -> u8 {
<Assets<T> as MetadataInspect<AccountIdOf<T>>>::decimals(id)
}
}
}
22 changes: 22 additions & 0 deletions pallets/api/src/fungibles/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::{mock::*, Error};
use frame_support::{assert_noop, assert_ok};

// https://docs.substrate.io/test/

#[test]
fn it_works_for_default_value() {
new_test_ext().execute_with(|| {
// Dispatch a signed extrinsic.
assert_ok!(fungibles::do_something(RuntimeOrigin::signed(1), 42));
// Read pallet storage and assert an expected result.
assert_eq!(fungibles::something(), Some(42));
});
}

#[test]
fn correct_error_for_none_value() {
new_test_ext().execute_with(|| {
// Ensure the expected error is thrown when no value is present.
assert_noop!(fungibles::cause_error(RuntimeOrigin::signed(1)), Error::<Test>::NoneValue);
});
}
3 changes: 3 additions & 0 deletions pallets/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#![cfg_attr(not(feature = "std"), no_std)]

pub mod fungibles;
60 changes: 60 additions & 0 deletions pallets/api/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use frame_support::{parameter_types, traits::Everything};
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved
use frame_system as system;
use sp_core::H256;
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup},
BuildStorage,
};

type Block = frame_system::mocking::MockBlock<Test>;

// Configure a mock runtime to test the pallet.
frame_support::construct_runtime!(
pub enum Test
{
System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
fungibles: crate::{Pallet, Call, Storage, Event<T>},
}
);

parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const SS58Prefix: u8 = 42;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl system::Config for Test {
type BaseCallFilter = Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Nonce = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = SS58Prefix;
type OnSetCode = ();
type MaxConsumers = frame_support::traits::ConstU32<16>;
}

impl crate::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
}

// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
}
11 changes: 4 additions & 7 deletions pop-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pop-api"
description = "Easily access the power of Polkadot via the Pop Network"
description = "Enabling smart(er) contracts with the power of Polkadot"
license = "GPL-3.0-only"
version = "0.0.0"
edition = "2021"
Expand All @@ -10,7 +10,7 @@ enumflags2 = { version = "0.7.7" }
ink = { version = "5.0.0", default-features = false }
sp-io = { version = "31.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] }

pop-primitives = { path = "../primitives", default-features = false }
primitives = { path = "../primitives", default-features = false }
Daanvdplas marked this conversation as resolved.
Show resolved Hide resolved

[lib]
name = "pop_api"
Expand All @@ -22,10 +22,7 @@ default = ["std"]
std = [
"enumflags2/std",
"ink/std",
"pop-primitives/std",
"primitives/std",
"sp-io/std",
]
assets = ["pop-primitives/assets"]
balances = []
nfts = ["pop-primitives/nfts"]
cross-chain = ["pop-primitives/cross-chain"]
fungibles = ["primitives/fungibles"]
Loading
Loading