Skip to content

Commit

Permalink
feat: query_state & pop_api_primitives (#14)
Browse files Browse the repository at this point in the history
* demo_contract:query_runtime

* pop-api-ext:query

* read relay_block# & state

* unused import

* ci: add build workflow

* chore: improve zombienet config

* ci: grant write permission to actions

* feat: initialize pop-api directory

* feat: add codec indexes, and wrap NFT interface

* feat: add example contract utilizing pop-api crate -- not working

* feat: minting NFT from contract through runtime works

* feat: generic state reads

* chore: fmt

* RuntimeStateKeys

* clean keys

* fmt

* cumulus_parachain_system is accessible

---------

Co-authored-by: Frank Bell <[email protected]>
Co-authored-by: Peter White <[email protected]>
  • Loading branch information
3 people authored Mar 4, 2024
1 parent 275f487 commit 64ba54d
Show file tree
Hide file tree
Showing 18 changed files with 442 additions and 53 deletions.
12 changes: 11 additions & 1 deletion Cargo.lock

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

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ substrate-wasm-builder = { git = "https://github.com/paritytech/polkadot-sdk", b
substrate-build-script-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.7.1" }

# Local

pop-runtime = { path = "./runtime" }
pop-api-primitives = { path = "./pop-api/primitives"}

# Substrate
sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.7.1" }
Expand Down
1 change: 0 additions & 1 deletion contracts/pop-api-examples/balance-transfer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"

[dependencies]
ink = { version = "4.3.0", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

Expand Down
9 changes: 7 additions & 2 deletions contracts/pop-api-examples/balance-transfer/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]


use ink::{
env::Environment,
prelude::vec::Vec,
};

use ink::primitives::AccountId;
use sp_runtime::MultiAddress;
use scale::{Encode, Decode};
use scale_info::TypeInfo;

/// A part of the runtime dispatchable API.
///
Expand All @@ -19,7 +20,7 @@ use sp_runtime::MultiAddress;
/// You can investigate the full `RuntimeCall` definition by either expanding
/// `construct_runtime!` macro application or by using secondary tools for reading chain
/// metadata, like `subxt`.
#[derive(scale::Encode)]
#[derive(Encode, Decode, TypeInfo)]
enum RuntimeCall {
/// This index can be found by investigating runtime configuration. You can check the
/// pallet order inside `construct_runtime!` block and read the position of your
Expand Down Expand Up @@ -80,6 +81,10 @@ pub trait PopApi {
/// operations.
#[ink(extension = 0xfecb)]
fn dispatch(call: RuntimeCall) -> Result<Vec<u8>>;

#[ink(extension = 0xfeca)]
fn read_state(key: SafeKeys) -> Result<Vec<u8>>;

}

impl ink::env::chain_extension::FromStatusCode for PopApiError {
Expand Down
9 changes: 9 additions & 0 deletions contracts/pop-api-examples/read-runtime-state/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Ignore build artifacts from the local tests sub-crate.
/target/

# Ignore backup files creates by cargo fmt.
**/*.rs.bk

# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
33 changes: 33 additions & 0 deletions contracts/pop-api-examples/read-runtime-state/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[package]
name = "pop_api_extension_demo"
version = "0.1.0"
authors = ["[your_name] <[your_email]>"]
edition = "2021"

[dependencies]
ink = { version = "4.3.0", default-features = false }
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

pop-api = { path = "../../../pop-api", default-features = false }

sp-io = { version = "23.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] }
sp-runtime = { version = "24.0.0", default-features = false }

[dev-dependencies]
ink_e2e = "4.3.0"

[lib]
path = "lib.rs"

[features]
default = ["std"]
std = [
"ink/std",
"scale/std",
"scale-info/std",
"sp-runtime/std",
"sp-io/std",
]
ink-as-dependency = []
e2e-tests = []
224 changes: 224 additions & 0 deletions contracts/pop-api-examples/read-runtime-state/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#![cfg_attr(not(feature = "std"), no_std, no_main)]

use ink::{
env::Environment,
prelude::vec::Vec,
};

use ink::primitives::AccountId;
use sp_runtime::MultiAddress;
use pop_api::storage_keys::RuntimeStateKeys::ParachainSystemKeys;


#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum PopApiError {
TotalSupplyFailed,
}

pub type Result<T> = core::result::Result<T, PopApiError>;

use scale;
impl From<scale::Error> for PopApiError {
fn from(_: scale::Error) -> Self {
panic!("encountered unexpected invalid SCALE encoding")
}
}

/// This is an example of how an ink! contract may call the Substrate
/// runtime function `RandomnessCollectiveFlip::random_seed`. See the
/// file `runtime/chain-extension-example.rs` for that implementation.
///
/// Here we define the operations to interact with the Substrate runtime.
#[ink::chain_extension]
pub trait PopApi {
type ErrorCode = PopApiError;

/// Note: this gives the operation a corresponding `func_id` (1101 in this case),
/// and the chain-side chain extension will get the `func_id` to do further
/// operations.
#[ink(extension = 0xfeca)]
fn read_relay_block_number(key: LastRelayChainBlockNumber) -> Result<BlockNumber>;

}

impl ink::env::chain_extension::FromStatusCode for PopApiError {
fn from_status_code(status_code: u32) -> core::result::Result<(), Self> {
match status_code {
0 => Ok(()),
1 => Err(Self::TotalSupplyFailed),
_ => panic!("encountered unknown status code"),
}
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
pub enum CustomEnvironment {}

impl Environment for CustomEnvironment {
const MAX_EVENT_TOPICS: usize =
<ink::env::DefaultEnvironment as Environment>::MAX_EVENT_TOPICS;

type AccountId = <ink::env::DefaultEnvironment as Environment>::AccountId;
type Balance = <ink::env::DefaultEnvironment as Environment>::Balance;
type Hash = <ink::env::DefaultEnvironment as Environment>::Hash;
type BlockNumber = <ink::env::DefaultEnvironment as Environment>::BlockNumber;
type Timestamp = <ink::env::DefaultEnvironment as Environment>::Timestamp;

type ChainExtension = PopApi;
}

#[ink::contract(env = crate::CustomEnvironment)]
mod pop_api_extension_demo {
use crate::{
BalancesCall,
RuntimeCall,
};

use super::PopApiError;

use ink::env::Error as EnvError;

#[ink(event)]
pub struct RelayBlockNumberRead {
value: BlockNumber
}

/// A trivial contract with a single message, that uses `call-runtime` API for
/// performing native token transfer.
#[ink(storage)]
#[derive(Default)]
pub struct PopApiExtensionDemo;

impl From<EnvError> for PopApiError {
fn from(e: EnvError) -> Self {
match e {
EnvError::CallRuntimeFailed => PopApiError::TotalSupplyFailed,
_ => panic!("Unexpected error from `pallet-contracts`."),
}
}
}

impl PopApiExtensionDemo {
#[ink(constructor, payable)]
pub fn new() -> Self {
ink::env::debug_println!("PopApiExtensionDemo::new");
Default::default()
}

#[ink(message)]
pub fn read_relay_block_number(
&self
) {
let state = self.env().extension().read_state(LastRelayChainBlockNumber);
ink::env::debug_println!("{:?}", state);
ink::env().emit_event(
RelayBlockNumberRead {value: state}
);
}
}

#[cfg(all(test, feature = "e2e-tests"))]
mod e2e_tests {
use super::*;
use ink_e2e::{
ChainBackend,
ContractsBackend,
};

use ink::{
env::{
test::default_accounts,
DefaultEnvironment,
},
primitives::AccountId,
};

type E2EResult<T> = Result<T, Box<dyn std::error::Error>>;

/// The base number of indivisible units for balances on the
/// `substrate-contracts-node`.
const UNIT: Balance = 1_000_000_000_000;

/// The contract will be given 1000 tokens during instantiation.
const CONTRACT_BALANCE: Balance = 1_000 * UNIT;

/// The receiver will get enough funds to have the required existential deposit.
///
/// If your chain has this threshold higher, increase the transfer value.
const TRANSFER_VALUE: Balance = 1 / 10 * UNIT;

/// An amount that is below the existential deposit, so that a transfer to an
/// empty account fails.
///
/// Must not be zero, because such an operation would be a successful no-op.
const INSUFFICIENT_TRANSFER_VALUE: Balance = 1;

/// Positive case scenario:
/// - the call is valid
/// - the call execution succeeds
#[ink_e2e::test]
async fn transfer_with_call_runtime_works<Client: E2EBackend>(
mut client: Client,
) -> E2EResult<()> {
// given
let mut constructor = RuntimeCallerRef::new();
let contract = client
.instantiate("call-runtime", &ink_e2e::alice(), &mut constructor)
.value(CONTRACT_BALANCE)
.submit()
.await
.expect("instantiate failed");
let mut call_builder = contract.call_builder::<RuntimeCaller>();

let accounts = default_accounts::<DefaultEnvironment>();

let receiver: AccountId = accounts.bob;

let sender_balance_before = client
.free_balance(accounts.alice)
.await
.expect("Failed to get account balance");
let receiver_balance_before = client
.free_balance(receiver)
.await
.expect("Failed to get account balance");

// when
let transfer_message =
call_builder.transfer_through_runtime(receiver, TRANSFER_VALUE);

let call_res = client
.call(&ink_e2e::alice(), &transfer_message)
.submit()
.await
.expect("call failed");

assert!(call_res.return_value().is_ok());

// then
let sender_balance_after = client
.free_balance(accounts.alice)
.await
.expect("Failed to get account balance");
let receiver_balance_after = client
.free_balance(receiver)
.await
.expect("Failed to get account balance");

assert_eq!(
contract_balance_before,
contract_balance_after + TRANSFER_VALUE
);
assert_eq!(
receiver_balance_before,
receiver_balance_after - TRANSFER_VALUE
);

Ok(())
}

}
}
5 changes: 4 additions & 1 deletion pop-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ version = "0.0.0"
edition = "2021"

[dependencies]

pop-api-primitives = { path = "./primitives" }

ink = { version = "4.3.0", default-features = false }
ink_env = { version = "4.3.0", default-features = false }
sp-runtime = { version = "24.0.0", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

Expand Down
28 changes: 28 additions & 0 deletions pop-api/primitives/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "pop-api-primitives"
description = "Reserved crate for pop-api-primitives."
license = "GPL-3.0-only"
version = "0.0.0"
edition = "2021"

[dependencies]
sp-runtime = { version = "24.0.0", default-features = false }

scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true }

[lib]
name = "pop_api_primitives"
path = "src/lib.rs"
crate-type = [
"rlib",
]


[features]
default = ["std"]
std = [
"sp-runtime/std",
"scale/std",
"scale-info/std",
]
1 change: 1 addition & 0 deletions pop-api/primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod storage_keys;
Loading

0 comments on commit 64ba54d

Please sign in to comment.