Skip to content

Use AssetHolder instead of PortfolioId#1890

Open
HenriqueNogara wants to merge 15 commits intodevelop_v8from
new-portfolio
Open

Use AssetHolder instead of PortfolioId#1890
HenriqueNogara wants to merge 15 commits intodevelop_v8from
new-portfolio

Conversation

@HenriqueNogara
Copy link
Contributor

@HenriqueNogara HenriqueNogara commented Feb 24, 2026

changelog

modified external API

Replace PortfolioId for `AssetHolder;

  • Asset Pallet:

    • Events: ControllerTransfer, AssetBalanceUpdated;
    • Extrinsics: issue, redeem, controller_transfer;
    • New Errors: InvalidTransferSenderDidMatchesReceiverDid, UnauthorizedHolderKey, KeyNotFoundForDid, InsufficientTokensLocked;
  • Identity Pallet:

    • New Errors: IdentityNotFoundForAccountPortfolio;
  • NFT Pallet:

    • Events: NFTHoldingsUpdated
    • Storage: Remove NFTOwner;
    • Extrinsics: issue_nft, redeem_nft, controller_transfer;
    • New Errors: NFTIsNotLocked;
  • Settlement Pallet:

    • Events: InstructionAffirmed, AffirmationWithdrawn, InstructionAutomaticallyAffirmed;
    • Constants: replace MaxNumberOfPortfolios for MaxNumberOfAssetHolders;
    • Storage: AffirmsReceived, UserAffirmations;
    • Extrinsics: affirm_with_receipts, execute_manual_instruction, add_and_affirm_instruction, affirm_instruction, withdraw_affirmation, reject_instruction, affirm_with_receipts_with_count, affirm_instruction_with_count, reject_instruction_with_count, withdraw_affirmation_with_count, add_and_affirm_with_mediators,
  • Runtime Api

    • asset: transfer_report;
    • nft: transfer_report;
    • settlement: get_affirmation_count

@HenriqueNogara HenriqueNogara changed the title New portfolio New Portfolio Type Feb 24, 2026
@HenriqueNogara HenriqueNogara changed the title New Portfolio Type Use AssetHolder instead of PortfolioId Mar 2, 2026
return weight;
}

Weight::zero()
Copy link

Choose a reason for hiding this comment

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

should we count reads/writes and return actual weight here? migrate_to_v7 also returns zero weight

polymesh_primitives::storage_migrate_on!(StorageVersion::<T>, 4, {
migrations::migrate_to_v4::<T>();
});
Weight::zero()
Copy link

Choose a reason for hiding this comment

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

should we return the actual weight here? migrate_to_v4 actually counts reads/writes but we just return zero

)?;

let updated_balance = BalanceOf::<T>::get(asset_id, portfolio.did) - value;
let updated_did_balance = BalanceOf::<T>::get(asset_id, holder_did) - value;
Copy link

Choose a reason for hiding this comment

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

should we use checked_sub for substraction here?

Copy link
Contributor

Choose a reason for hiding this comment

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

If the code checks the available balance before this, then we can just use saturating_* instead of checked_* ops.

&[(issuer_did, current_issuer_balance)],
)?;

let new_issuer_balance = current_issuer_balance + amount_to_issue;
Copy link

Choose a reason for hiding this comment

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

should we avoid unchecked operations and use checked_add instead?

pub type Error = Vec<u8>;

sp_api::decl_runtime_apis! {
#[api_version(4)]
Copy link

Choose a reason for hiding this comment

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

should we bump api_version because of the changes?


sp_api::decl_runtime_apis! {

#[api_version(2)]
Copy link

Choose a reason for hiding this comment

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

should we bump api_version because of the changes?

use polymesh_primitives::AssetHolder;

sp_api::decl_runtime_apis! {
#[api_version(2)]
Copy link

Choose a reason for hiding this comment

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

should we bump api_version because of the changes?

None,
);

if let Some(cursor) = result.maybe_cursor {
Copy link

Choose a reason for hiding this comment

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

should we instead loop until maybe_cursor returns None?

Copy link
Contributor

@Neopallium Neopallium left a comment

Choose a reason for hiding this comment

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

First round of review.

- run:
name: Upgrade chain to current version.
command: cargo run --locked --bin tools -- upgrade_chain ../assets/ci-runtime/polymesh_runtime_develop.compact.compressed.wasm
command: cargo run --features current_release --locked --bin tools -- upgrade_chain ../assets/ci-runtime/polymesh_runtime_develop.compact.compressed.wasm
Copy link
Contributor

Choose a reason for hiding this comment

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

Since this needs to run/compile against the old chain, we need to use previous_release here.

Comment on lines -49 to +50
sender_portfolio: PortfolioId,
receiver_portfolio: PortfolioId,
sender: AssetHolder,
receiver: AssetHolder,
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to bump the version of this Runtime API method.

Comment on lines 42 to +44
fn transfer_report(
sender_portfolio: PortfolioId,
receiver_portfolio: PortfolioId,
sender: AssetHolder,
receiver: AssetHolder,
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here. Bump the version. And the other Runtime API methods below.

Comment on lines 66 to 74
fn transfer_report(
&self,
sender_portfolio: PortfolioId,
receiver_portfolio: PortfolioId,
sender: AssetHolder,
receiver: AssetHolder,
asset_id: AssetId,
transfer_value: Balance,
skip_locked_check: bool,
at: Option<<Block as BlockT>::Hash>,
) -> RpcResult<Vec<DispatchError>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would like to start dropping these RPC endpoints, since it is much better for clients to directly call the Runtime APIs instead. Otherwise this RPC impl. needs to be updated to translate the new AssetHolder type to the old Runtime API call which only supports PortfolioId.

Comment on lines +3619 to +3622
let primary_key = pallet_identity::Pallet::<T>::get_primary_key(primary_did)
.ok_or(Error::<T>::KeyNotFoundForDid)?;

ensure!(primary_key == account_id, Error::<T>::UnauthorizedHolderKey);
Copy link
Contributor

Choose a reason for hiding this comment

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

If secondary_key is None the caller should always be the primary key. We could avoid looking up the DID's primary key by checking the caller account.

Comment on lines +3512 to +3515
/// - The transfer is between different DIDs;
/// - If [`AssetHolder::Portfolio`], it must exist;
/// - The sender has sufficient balance for the transfer (taking into account locks).
pub fn ensure_valid_holdings(
Copy link
Contributor

Choose a reason for hiding this comment

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

We might have to allow transfers (settlements) when the sender/receiver are the same DID. Otherwise this is going to trip up integrator. If the sender/receiver DIDs are the same, we can skip the more expensive compliance checks and stats updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants