Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Return account's asset balances #13352

Merged
merged 13 commits into from Mar 2, 2023
34 changes: 34 additions & 0 deletions bin/node/runtime/src/assets_api.rs
@@ -0,0 +1,34 @@
// This file is part of Substrate.

// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Runtime API definition for assets.

use codec::Codec;
use sp_std::vec::Vec;

sp_api::decl_runtime_apis! {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this re-defined in every single runtime in Cumulus?

Copy link
Contributor

Choose a reason for hiding this comment

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

there is just one common definition for all statemine/t runtimes (statemine/statemint/westmint),
but lets discuss it here: paritytech/cumulus#2180 (comment)

pub trait AssetsApi<AccountId, AssetBalance, AssetId>
where
AccountId: Codec,
AssetBalance: Codec,
AssetId: Codec,
{
/// Returns the list of `AssetId`s and corresponding balance that an `AccountId` has.
fn account_balances(account: AccountId) -> Vec<(AssetId, AssetBalance)>;
}
}
15 changes: 15 additions & 0 deletions bin/node/runtime/src/lib.rs
Expand Up @@ -105,6 +105,9 @@ use sp_runtime::generic::Era;
/// Generated voter bag information.
mod voter_bags;

/// Runtime API definition for assets.
pub mod assets_api;

// Make the WASM binary available.
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
Expand Down Expand Up @@ -2072,6 +2075,18 @@ impl_runtime_apis! {
}
}

impl assets_api::AssetsApi<
Block,
AccountId,
Balance,
u32,
> for Runtime
{
fn account_balances(account: AccountId) -> Vec<(u32, Balance)> {
Assets::account_balances(account)
}
}

impl pallet_contracts::ContractsApi<Block, AccountId, Balance, BlockNumber, Hash> for Runtime
{
fn call(
Expand Down
7 changes: 7 additions & 0 deletions frame/assets/src/functions.rs
Expand Up @@ -924,4 +924,11 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
Ok(())
})
}

/// Returns all the non-zero balances for all assets of the given `account`.
pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
pub fn account_balances(account: T::AccountId) -> Vec<(T::AssetId, T::Balance)> {
pub fn account_balances(account: T::AccountId, max_iters: Option<u32>) -> Vec<(T::AssetId, T::Balance)> {

Copy link
Contributor

Choose a reason for hiding this comment

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

Then the node implementing this can decide up to how many iterations it wants to support.

Copy link
Member

Choose a reason for hiding this comment

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

The node does not augments calls to the runtime. So, what ever calls this would need to set this value. But I'm fine with the idea in general

Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t think this approach will work in general. This will need to have proper paging support to be exhaustive and this kind of API is simply impossible for ERC20 tokens implementation.

Asset::<T, I>::iter_keys()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Asset::<T, I>::iter_keys()
Asset::<T, I>::iter_keys().take(max_iters.unwrap_or(u32::MAX))

.filter_map(|id| Self::maybe_balance(id, account.clone()).map(|balance| (id, balance)))
.collect::<Vec<_>>()
}
}
5 changes: 4 additions & 1 deletion frame/assets/src/tests.rs
Expand Up @@ -37,11 +37,14 @@ fn asset_ids() -> Vec<u32> {
fn basic_minting_should_work() {
new_test_ext().execute_with(|| {
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1));
assert_ok!(Assets::force_create(RuntimeOrigin::root(), 1, 1, true, 1));
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 1, 100));
assert_eq!(Assets::balance(0, 1), 100);
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 0, 2, 100));
assert_eq!(Assets::balance(0, 2), 100);
assert_eq!(asset_ids(), vec![0, 999]);
assert_eq!(asset_ids(), vec![0, 1, 999]);
assert_ok!(Assets::mint(RuntimeOrigin::signed(1), 1, 1, 100));
assert_eq!(Assets::account_balances(1), vec![(0, 100), (999, 100), (1, 100)]);
});
}

Expand Down