From 11b9b4ebc87c9b8c2a1e6761209c645958d242a3 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Sun, 30 Aug 2026 20:36:05 +0800 Subject: [PATCH] feat(multisig): carry the proposal's inner call on execute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runtime's `multisig.execute` now takes the call it dispatches: pub fn execute(origin, multisig_address, proposal_id, call: Box) The chain dispatches it only if it re-encodes to the payload stored at the proposal id — the same binding `approve` already enforces — so the executor's wallet displays and signs the actual call instead of an opaque proposal id. Unlike `approve`, which resubmits the stored bytes length-prefixed, `execute` carries the call inline. `CallDecoder.decodeRuntimeCall` decodes the stored payload back into a call, so a proposal the bundled metadata cannot read fails before signing rather than building a call the chain would reject. The inner call is decoded through `_decodeCall`, not handed to the generated codec, so it gets the same policy and nesting checks as every other call boundary. `multisig.execute` replaces `recovery.as_recovered` as the decoder's second inline-nesting entry point, and the tests that used as_recovered as their nesting vehicle move with it. Bindings regenerated against a dev node built from chain #675 (spec 147, tx 6) using the pinned Quantus polkadart fork, which also drops pallet-recovery: `RecoveryService` and the recovery describer go with it, and the cold wallet's as_recovered debug payload becomes a multisig execute. Metadata fixture and call corpus regenerated from the same node. Spec 147 raises the normal-class base extrinsic weight from 108_157_000 to 767_297_000, so the transaction fee test's reference values move with it. Documents in pubspec why the polkadart fork is pinned: upstream's generator emits a compact integer for the zero-width `MultiAddress::Index` field, and a codec that disagrees with the metadata about a field's width re-frames every byte after it — a clearsigning bypass. Tests: quantus_sdk 463, cold-wallet-app 270, mobile-app 424, all with --exclude-tags=native. --- cold-wallet-app/lib/debug/debug_payloads.dart | 15 +- .../transaction_submission_service.dart | 11 +- .../multisig_action_confirm_sheet.dart | 18 +- .../multisig_approve_confirm_sheet.dart | 2 +- .../multisig_execute_confirm_sheet.dart | 20 +- .../multisig_require_call_bytes_test.dart | 21 + .../generated/planck/pallets/multisig.dart | 32 +- .../generated/planck/pallets/recovery.dart | 338 --------- .../planck/pallets/reversible_transfers.dart | 9 + .../lib/generated/planck/pallets/system.dart | 16 +- .../planck/pallets/tech_referenda.dart | 2 +- .../lib/generated/planck/pallets/vesting.dart | 24 +- .../lib/generated/planck/pallets/zk_tree.dart | 27 + quantus_sdk/lib/generated/planck/planck.dart | 56 +- .../frame_metadata_hash_extension/mode.dart | 1 + .../dispatch/dispatch_class.dart | 1 + .../types/frame_support/dispatch/pays.dart | 1 + .../traits/tokens/misc/balance_status.dart | 1 + .../types/frame_system/pallet/error.dart | 1 + .../types/pallet_balances/pallet/error.dart | 1 + .../pallet/unexpected_kind.dart | 1 + .../types/pallet_balances/types/reasons.dart | 1 + .../types/pallet_multisig/pallet/call.dart | 64 +- .../types/pallet_multisig/pallet/error.dart | 8 +- .../pallet_multisig/proposal_status.dart | 1 + .../types/pallet_preimage/pallet/error.dart | 1 + .../pallet_preimage/pallet/hold_reason.dart | 1 + .../pallet/error.dart | 1 + .../pallet_recovery/active_recovery.dart | 76 -- .../types/pallet_recovery/deposit_kind.dart | 135 ---- .../types/pallet_recovery/pallet/call.dart | 653 ------------------ .../types/pallet_recovery/pallet/error.dart | 127 ---- .../types/pallet_recovery/pallet/event.dart | 477 ------------- .../pallet_recovery/recovery_config.dart | 89 --- .../types/pallet_referenda/pallet/error.dart | 1 + .../pallet/call.dart | 9 + .../pallet/error.dart | 1 + .../pallet/hold_reason.dart | 1 + .../types/pallet_scheduler/pallet/error.dart | 1 + .../pallet_transaction_payment/releases.dart | 1 + .../types/pallet_treasury/pallet/error.dart | 1 + .../types/pallet_utility/pallet/error.dart | 1 + .../types/pallet_utility/pallet/event.dart | 1 + .../types/pallet_vesting/pallet/call.dart | 24 +- .../types/pallet_vesting/pallet/error.dart | 20 +- .../types/pallet_vesting/pallet/event.dart | 31 +- .../types/pallet_wormhole/pallet/error.dart | 7 +- .../types/pallet_zk_tree/pallet/error.dart | 9 +- .../types/pallet_zk_tree/pallet/event.dart | 42 +- .../types/quantus_runtime/runtime_call.dart | 82 +-- .../types/quantus_runtime/runtime_event.dart | 91 +-- .../types/sp_arithmetic/arithmetic_error.dart | 1 + .../sp_runtime/proving_trie/trie_error.dart | 1 + .../planck/types/sp_runtime/token_error.dart | 1 + .../types/sp_runtime/transactional_error.dart | 1 + quantus_sdk/lib/quantus_sdk.dart | 1 - quantus_sdk/lib/src/chain/call_decoder.dart | 158 ++--- quantus_sdk/lib/src/chain/call_policy.dart | 16 +- .../lib/src/constants/app_constants.dart | 4 +- .../lib/src/services/multisig_service.dart | 31 +- .../lib/src/services/recovery_service.dart | 309 --------- quantus_sdk/lib/src/testing/call_corpus.dart | 114 +-- quantus_sdk/pubspec.yaml | 11 +- quantus_sdk/test/chain/call_decoder_test.dart | 78 ++- quantus_sdk/test/chain/call_policy_test.dart | 7 +- .../test/fixtures/planck_metadata.scale | Bin 108896 -> 101141 bytes quantus_sdk/test/multisig_service_test.dart | 59 +- .../test/quantus_payload_parser_test.dart | 16 +- .../test/services/transaction_fee_test.dart | 13 +- 69 files changed, 607 insertions(+), 2769 deletions(-) create mode 100644 mobile-app/test/unit/multisig_require_call_bytes_test.dart delete mode 100644 quantus_sdk/lib/generated/planck/pallets/recovery.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/active_recovery.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/deposit_kind.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/call.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/error.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/event.dart delete mode 100644 quantus_sdk/lib/generated/planck/types/pallet_recovery/recovery_config.dart delete mode 100644 quantus_sdk/lib/src/services/recovery_service.dart diff --git a/cold-wallet-app/lib/debug/debug_payloads.dart b/cold-wallet-app/lib/debug/debug_payloads.dart index 785b8f717..c51784c16 100644 --- a/cold-wallet-app/lib/debug/debug_payloads.dart +++ b/cold-wallet-app/lib/debug/debug_payloads.dart @@ -4,7 +4,6 @@ import 'package:convert/convert.dart'; import 'package:polkadart/scale_codec.dart'; import 'package:quantus_sdk/generated/planck/pallets/balances.dart' as balances_pallet; import 'package:quantus_sdk/generated/planck/pallets/multisig.dart' as multisig_pallet; -import 'package:quantus_sdk/generated/planck/pallets/recovery.dart' as recovery_pallet; import 'package:quantus_sdk/generated/planck/pallets/tech_collective.dart' as collective_pallet; import 'package:quantus_sdk/generated/planck/pallets/utility.dart' as utility_pallet; import 'package:quantus_sdk/generated/planck/types/sp_runtime/multiaddress/multi_address.dart' as multi_address; @@ -52,7 +51,7 @@ class DebugPayloads { final seen = {}; for (final call in [...callCorpus.entries.map(_fromCorpus), ..._composed]) { // The corpus repeats an identical encoding once per nested-call variant - // (every `as_recovered [call=…]` is the same bytes); one row is enough. + // (every `execute [call=…]` is the same bytes); one row is enough. if (!seen.add(hex.encode(call.call))) continue; grouped.putIfAbsent(call.pallet, () => []).add(call); } @@ -74,8 +73,8 @@ class DebugPayloads { /// The wrapper calls the corpus cannot express: it fills every nested call /// slot with the same three-byte `System.remark`, so the screens that lift an - /// inner transfer into the hero position — a multisig proposal or approval, a - /// batch, a recovered-account dispatch — are only reachable from here. + /// inner transfer into the hero position — a multisig proposal, approval or + /// execution, a batch — are only reachable from here. static final List _composed = [ DebugCall( pallet: 'Multisig', @@ -112,10 +111,10 @@ class DebugPayloads { .encode(), ), DebugCall( - pallet: 'Recovery', - label: 'as_recovered [carrying a transfer]', - call: const recovery_pallet.Txs() - .asRecovered(account: _address(AppConstants.debugTestAddress), call: _send(_tokens(3))) + pallet: 'Multisig', + label: 'execute [carrying a transfer]', + call: const multisig_pallet.Txs() + .execute(multisigAddress: _debugMultisigAccount, proposalId: 12, call: _send(_tokens(3))) .encode(), ), ]; diff --git a/mobile-app/lib/services/transaction_submission_service.dart b/mobile-app/lib/services/transaction_submission_service.dart index f74bb29bc..c83f4bb1c 100644 --- a/mobile-app/lib/services/transaction_submission_service.dart +++ b/mobile-app/lib/services/transaction_submission_service.dart @@ -280,6 +280,7 @@ class TransactionSubmissionService { required MultisigAccount msig, required Account signer, required MultisigProposal proposal, + List? callBytes, BigInt? fee, }) async { final pending = PendingMultisigExecutionEvent.fromProposal( @@ -293,7 +294,7 @@ class TransactionSubmissionService { TelemetryService().sendEvent('multisig_execute'); - await _submitExecute(msig: msig, signer: signer, proposalId: proposal.id, pending: pending); + await _submitExecute(msig: msig, signer: signer, proposalId: proposal.id, callBytes: callBytes, pending: pending); } Future _submitExecute({ @@ -301,10 +302,16 @@ class TransactionSubmissionService { required Account signer, required int proposalId, required PendingMultisigExecutionEvent pending, + List? callBytes, }) async { try { final service = _ref.read(multisigServiceProvider); - final hashBytes = await service.submitExecuteExtrinsic(msig: msig, signer: signer, proposalId: proposalId); + final hashBytes = await service.submitExecuteExtrinsic( + msig: msig, + signer: signer, + proposalId: proposalId, + callBytes: callBytes, + ); final extrinsicHash = '0x${hex.encode(hashBytes)}'; quantusPrint('[Execute] submitted: $extrinsicHash'); diff --git a/mobile-app/lib/v2/screens/multisig/multisig_action_confirm_sheet.dart b/mobile-app/lib/v2/screens/multisig/multisig_action_confirm_sheet.dart index b16036dce..58d92b44e 100644 --- a/mobile-app/lib/v2/screens/multisig/multisig_action_confirm_sheet.dart +++ b/mobile-app/lib/v2/screens/multisig/multisig_action_confirm_sheet.dart @@ -32,12 +32,22 @@ typedef MultisigConfirmCallBuilder = RuntimeCall Function(Account signer, List> Function(WidgetRef ref); +/// The call bytes an action that resubmits the proposal cannot proceed without. +/// +/// [MultisigConfirmCallBuilder] takes them as nullable because actions carrying +/// only a proposal reference have none; [action] names the caller so a missing +/// load is not mistaken for a chain rejection. +List requireCallBytes(List? callBytes, String action) { + if (callBytes == null) throw StateError('$action requires the proposal call bytes'); + return callBytes; +} + /// Submits a hardware-signed extrinsic for the action. typedef MultisigConfirmExternalSubmitter = Future Function( diff --git a/mobile-app/lib/v2/screens/multisig/multisig_approve_confirm_sheet.dart b/mobile-app/lib/v2/screens/multisig/multisig_approve_confirm_sheet.dart index 4e064fd52..53c0b5a49 100644 --- a/mobile-app/lib/v2/screens/multisig/multisig_approve_confirm_sheet.dart +++ b/mobile-app/lib/v2/screens/multisig/multisig_approve_confirm_sheet.dart @@ -45,7 +45,7 @@ void showMultisigApproveConfirmSheet( buildCall: (resolvedSigner, callBytes) => MultisigService().buildApproveCall( msig: msig, proposalId: proposal.id, - call: callBytes ?? (throw StateError('Approve requires the proposal call bytes')), + call: requireCallBytes(callBytes, 'Approve'), ), submit: (ref, resolvedSigner, fee, callBytes) => ref .read(transactionSubmissionServiceProvider) diff --git a/mobile-app/lib/v2/screens/multisig/multisig_execute_confirm_sheet.dart b/mobile-app/lib/v2/screens/multisig/multisig_execute_confirm_sheet.dart index f1fcd8cc5..095eb5501 100644 --- a/mobile-app/lib/v2/screens/multisig/multisig_execute_confirm_sheet.dart +++ b/mobile-app/lib/v2/screens/multisig/multisig_execute_confirm_sheet.dart @@ -28,17 +28,23 @@ void showMultisigExecuteConfirmSheet( authReason: (l10n) => l10n.multisigExecuteAuthReason, failedMessage: (l10n) => l10n.multisigExecuteFailed, ), - // Executing dispatches the stored call, so what the signer reviews comes - // from chain storage, not the indexer; a proposal no longer in storage - // could not execute anyway. + // Executing resubmits the proposal's inner call, which the chain compares + // byte-for-byte against what it stored — so the bytes come from chain + // storage, not the indexer, and the signer reviews the call that will + // dispatch rather than an opaque proposal id. loadCallBytes: (ref) => ref.read(multisigServiceProvider).fetchProposalCallBytes(msig: msig, proposalId: proposal.id), - estimateFee: (ref, signer, callBytes) => - ref.read(multisigServiceProvider).estimateExecuteFee(msig: msig, signer: signer, proposalId: proposal.id), - buildCall: (signer, callBytes) => MultisigService().buildExecuteCall(msig: msig, proposalId: proposal.id), + estimateFee: (ref, signer, callBytes) => ref + .read(multisigServiceProvider) + .estimateExecuteFee(msig: msig, signer: signer, proposalId: proposal.id, callBytes: callBytes), + buildCall: (signer, callBytes) => MultisigService().buildExecuteCall( + msig: msig, + proposalId: proposal.id, + call: requireCallBytes(callBytes, 'Execute'), + ), submit: (ref, signer, fee, callBytes) => ref .read(transactionSubmissionServiceProvider) - .executeProposal(msig: msig, signer: signer, proposal: proposal, fee: fee), + .executeProposal(msig: msig, signer: signer, proposal: proposal, callBytes: callBytes, fee: fee), submitExternal: (ref, {required signer, required unsignedData, required signature, required publicKey, fee}) => ref .read(transactionSubmissionServiceProvider) diff --git a/mobile-app/test/unit/multisig_require_call_bytes_test.dart b/mobile-app/test/unit/multisig_require_call_bytes_test.dart new file mode 100644 index 000000000..9f3be408a --- /dev/null +++ b/mobile-app/test/unit/multisig_require_call_bytes_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:resonance_network_wallet/v2/screens/multisig/multisig_action_confirm_sheet.dart'; + +void main() { + group('requireCallBytes', () { + test('returns the bytes the sheet loaded', () { + const bytes = [1, 2, 3]; + expect(requireCallBytes(bytes, 'Execute'), bytes); + }); + + // Approve and execute are both refused by the chain unless they carry the + // stored call, so a sheet that reached the builder without it must fail + // loudly rather than submit something the chain will reject. + test('names the action when the bytes never loaded', () { + expect( + () => requireCallBytes(null, 'Execute'), + throwsA(isA().having((e) => e.message, 'message', contains('Execute'))), + ); + }); + }); +} diff --git a/quantus_sdk/lib/generated/planck/pallets/multisig.dart b/quantus_sdk/lib/generated/planck/pallets/multisig.dart index 1cbd394ac..f3aa8a59f 100644 --- a/quantus_sdk/lib/generated/planck/pallets/multisig.dart +++ b/quantus_sdk/lib/generated/planck/pallets/multisig.dart @@ -104,7 +104,8 @@ class Txs { /// The multisig address is deterministically derived from: /// hash(pallet_id || sorted_signers || threshold || nonce) /// - /// Signers are automatically sorted before hashing, so order doesn't matter. + /// Signers are sorted before hashing, so order doesn't matter. + /// Duplicate accounts are rejected. /// /// Economic costs: /// - MultisigFee: burned immediately (spam prevention) @@ -199,20 +200,35 @@ class Txs { /// Can be called by any signer of the multisig once the proposal has reached /// the approval threshold (status = Approved). The proposal must not be expired. /// + /// The executor resubmits the proposal's inner call; execution proceeds only + /// if it is byte-equal to the payload stored at `proposal_id` — the same + /// binding `approve` enforces. This serves two purposes: + /// - **Clearsigning:** the executor's (hardware) wallet displays and signs the actual call + /// being dispatched, not an opaque proposal id. + /// - **Self-describing weight:** the executing extrinsic carries the inner call, so its + /// declared weight carries the inner call's own declared weight (refunded to actuals + /// post-dispatch) instead of reserving a flat `MaxInnerCallWeight`, and runtime + /// transaction extensions can inspect the inner call and price its side effects + /// (account-reap cleanup, transfer-proof recording) exactly as they do for directly + /// submitted calls. Nothing about the dispatch is invisible to pre-dispatch admission or + /// fees. (Only the bookkeeping term is reserved at `MaxCallSize`, since the stored bytes' + /// length is unknown pre-dispatch; the unused remainder is refunded.) + /// /// On execution: - /// - The call is decoded and dispatched as the multisig account + /// - The call is dispatched as the multisig account /// - Proposal is removed from storage /// - Deposit is returned to the proposer /// /// Parameters: /// - `multisig_address`: The multisig account /// - `proposal_id`: ID (nonce) of the proposal to execute - /// - /// Note: The weight charged includes both multisig bookkeeping and MaxInnerCallWeight. - /// Actual weight is refunded based on the inner call's post-dispatch info. - /// The inner call's weight is validated against MaxInnerCallWeight at propose time. - _i8.Multisig execute({required _i2.AccountId32 multisigAddress, required int proposalId}) { - return _i8.Multisig(_i9.Execute(multisigAddress: multisigAddress, proposalId: proposalId)); + /// - `call`: The proposal's inner call, byte-equal to the stored payload + _i8.Multisig execute({ + required _i2.AccountId32 multisigAddress, + required int proposalId, + required _i8.RuntimeCall call, + }) { + return _i8.Multisig(_i9.Execute(multisigAddress: multisigAddress, proposalId: proposalId, call: call)); } } diff --git a/quantus_sdk/lib/generated/planck/pallets/recovery.dart b/quantus_sdk/lib/generated/planck/pallets/recovery.dart deleted file mode 100644 index 4e0e81349..000000000 --- a/quantus_sdk/lib/generated/planck/pallets/recovery.dart +++ /dev/null @@ -1,338 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; -import 'dart:typed_data' as _i6; - -import 'package:polkadart/polkadart.dart' as _i1; - -import '../types/pallet_recovery/active_recovery.dart' as _i4; -import '../types/pallet_recovery/pallet/call.dart' as _i9; -import '../types/pallet_recovery/recovery_config.dart' as _i3; -import '../types/quantus_runtime/runtime_call.dart' as _i7; -import '../types/sp_core/crypto/account_id32.dart' as _i2; -import '../types/sp_runtime/multiaddress/multi_address.dart' as _i8; - -class Queries { - const Queries(this.__api); - - final _i1.StateApi __api; - - final _i1.StorageMap<_i2.AccountId32, _i3.RecoveryConfig> _recoverable = - const _i1.StorageMap<_i2.AccountId32, _i3.RecoveryConfig>( - prefix: 'Recovery', - storage: 'Recoverable', - valueCodec: _i3.RecoveryConfig.codec, - hasher: _i1.StorageHasher.twoxx64Concat(_i2.AccountId32Codec()), - ); - - final _i1.StorageDoubleMap<_i2.AccountId32, _i2.AccountId32, _i4.ActiveRecovery> _activeRecoveries = - const _i1.StorageDoubleMap<_i2.AccountId32, _i2.AccountId32, _i4.ActiveRecovery>( - prefix: 'Recovery', - storage: 'ActiveRecoveries', - valueCodec: _i4.ActiveRecovery.codec, - hasher1: _i1.StorageHasher.twoxx64Concat(_i2.AccountId32Codec()), - hasher2: _i1.StorageHasher.twoxx64Concat(_i2.AccountId32Codec()), - ); - - final _i1.StorageMap<_i2.AccountId32, _i2.AccountId32> _proxy = - const _i1.StorageMap<_i2.AccountId32, _i2.AccountId32>( - prefix: 'Recovery', - storage: 'Proxy', - valueCodec: _i2.AccountId32Codec(), - hasher: _i1.StorageHasher.blake2b128Concat(_i2.AccountId32Codec()), - ); - - /// The set of recoverable accounts and their recovery configuration. - _i5.Future<_i3.RecoveryConfig?> recoverable(_i2.AccountId32 key1, {_i1.BlockHash? at}) async { - final hashedKey = _recoverable.hashedKeyFor(key1); - final bytes = await __api.getStorage(hashedKey, at: at); - if (bytes != null) { - return _recoverable.decodeValue(bytes); - } - return null; /* Nullable */ - } - - /// Active recovery attempts. - /// - /// First account is the account to be recovered, and the second account - /// is the user trying to recover the account. - _i5.Future<_i4.ActiveRecovery?> activeRecoveries( - _i2.AccountId32 key1, - _i2.AccountId32 key2, { - _i1.BlockHash? at, - }) async { - final hashedKey = _activeRecoveries.hashedKeyFor(key1, key2); - final bytes = await __api.getStorage(hashedKey, at: at); - if (bytes != null) { - return _activeRecoveries.decodeValue(bytes); - } - return null; /* Nullable */ - } - - /// The list of allowed proxy accounts. - /// - /// Map from the user who can access it to the recovered account. - _i5.Future<_i2.AccountId32?> proxy(_i2.AccountId32 key1, {_i1.BlockHash? at}) async { - final hashedKey = _proxy.hashedKeyFor(key1); - final bytes = await __api.getStorage(hashedKey, at: at); - if (bytes != null) { - return _proxy.decodeValue(bytes); - } - return null; /* Nullable */ - } - - /// The set of recoverable accounts and their recovery configuration. - _i5.Future> multiRecoverable(List<_i2.AccountId32> keys, {_i1.BlockHash? at}) async { - final hashedKeys = keys.map((key) => _recoverable.hashedKeyFor(key)).toList(); - final bytes = await __api.queryStorageAt(hashedKeys, at: at); - if (bytes.isNotEmpty) { - return bytes.first.changes.map((v) => _recoverable.decodeValue(v.key)).toList(); - } - return []; /* Nullable */ - } - - /// The list of allowed proxy accounts. - /// - /// Map from the user who can access it to the recovered account. - _i5.Future> multiProxy(List<_i2.AccountId32> keys, {_i1.BlockHash? at}) async { - final hashedKeys = keys.map((key) => _proxy.hashedKeyFor(key)).toList(); - final bytes = await __api.queryStorageAt(hashedKeys, at: at); - if (bytes.isNotEmpty) { - return bytes.first.changes.map((v) => _proxy.decodeValue(v.key)).toList(); - } - return []; /* Nullable */ - } - - /// Returns the storage key for `recoverable`. - _i6.Uint8List recoverableKey(_i2.AccountId32 key1) { - final hashedKey = _recoverable.hashedKeyFor(key1); - return hashedKey; - } - - /// Returns the storage key for `activeRecoveries`. - _i6.Uint8List activeRecoveriesKey(_i2.AccountId32 key1, _i2.AccountId32 key2) { - final hashedKey = _activeRecoveries.hashedKeyFor(key1, key2); - return hashedKey; - } - - /// Returns the storage key for `proxy`. - _i6.Uint8List proxyKey(_i2.AccountId32 key1) { - final hashedKey = _proxy.hashedKeyFor(key1); - return hashedKey; - } - - /// Returns the storage map key prefix for `recoverable`. - _i6.Uint8List recoverableMapPrefix() { - final hashedKey = _recoverable.mapPrefix(); - return hashedKey; - } - - /// Returns the storage map key prefix for `activeRecoveries`. - _i6.Uint8List activeRecoveriesMapPrefix(_i2.AccountId32 key1) { - final hashedKey = _activeRecoveries.mapPrefix(key1); - return hashedKey; - } - - /// Returns the storage map key prefix for `proxy`. - _i6.Uint8List proxyMapPrefix() { - final hashedKey = _proxy.mapPrefix(); - return hashedKey; - } -} - -class Txs { - const Txs(); - - /// Send a call through a recovered account. - /// - /// The dispatch origin for this call must be _Signed_ and registered to - /// be able to make calls on behalf of the recovered account. - /// - /// Parameters: - /// - `account`: The recovered account you want to make a call on-behalf-of. - /// - `call`: The call you want to make with the recovered account. - _i7.Recovery asRecovered({required _i8.MultiAddress account, required _i7.RuntimeCall call}) { - return _i7.Recovery(_i9.AsRecovered(account: account, call: call)); - } - - /// Allow ROOT to bypass the recovery process and set a rescuer account - /// for a lost account directly. - /// - /// The dispatch origin for this call must be _ROOT_. - /// - /// Parameters: - /// - `lost`: The "lost account" to be recovered. - /// - `rescuer`: The "rescuer account" which can call as the lost account. - _i7.Recovery setRecovered({required _i8.MultiAddress lost, required _i8.MultiAddress rescuer}) { - return _i7.Recovery(_i9.SetRecovered(lost: lost, rescuer: rescuer)); - } - - /// Create a recovery configuration for your account. This makes your account recoverable. - /// - /// Payment: `ConfigDepositBase` + `FriendDepositFactor` * #_of_friends balance - /// will be reserved for storing the recovery configuration. This deposit is returned - /// in full when the user calls `remove_recovery`. - /// - /// The dispatch origin for this call must be _Signed_. - /// - /// Parameters: - /// - `friends`: A list of friends you trust to vouch for recovery attempts. Should be - /// ordered and contain no duplicate values. - /// - `threshold`: The number of friends that must vouch for a recovery attempt before the - /// account can be recovered. Should be less than or equal to the length of the list of - /// friends. - /// - `delay_period`: The number of blocks after a recovery attempt is initialized that - /// needs to pass before the account can be recovered. - _i7.Recovery createRecovery({ - required List<_i2.AccountId32> friends, - required int threshold, - required int delayPeriod, - }) { - return _i7.Recovery(_i9.CreateRecovery(friends: friends, threshold: threshold, delayPeriod: delayPeriod)); - } - - /// Initiate the process for recovering a recoverable account. - /// - /// Payment: `RecoveryDeposit` balance will be reserved for initiating the - /// recovery process. This deposit will always be repatriated to the account - /// trying to be recovered. See `close_recovery`. - /// - /// The dispatch origin for this call must be _Signed_. - /// - /// Parameters: - /// - `account`: The lost account that you want to recover. This account needs to be - /// recoverable (i.e. have a recovery configuration). - _i7.Recovery initiateRecovery({required _i8.MultiAddress account}) { - return _i7.Recovery(_i9.InitiateRecovery(account: account)); - } - - /// Allow a "friend" of a recoverable account to vouch for an active recovery - /// process for that account. - /// - /// The dispatch origin for this call must be _Signed_ and must be a "friend" - /// for the recoverable account. - /// - /// Parameters: - /// - `lost`: The lost account that you want to recover. - /// - `rescuer`: The account trying to rescue the lost account that you want to vouch for. - /// - /// The combination of these two parameters must point to an active recovery - /// process. - _i7.Recovery vouchRecovery({required _i8.MultiAddress lost, required _i8.MultiAddress rescuer}) { - return _i7.Recovery(_i9.VouchRecovery(lost: lost, rescuer: rescuer)); - } - - /// Allow a successful rescuer to claim their recovered account. - /// - /// The dispatch origin for this call must be _Signed_ and must be a "rescuer" - /// who has successfully completed the account recovery process: collected - /// `threshold` or more vouches, waited `delay_period` blocks since initiation. - /// - /// Parameters: - /// - `account`: The lost account that you want to claim has been successfully recovered by - /// you. - _i7.Recovery claimRecovery({required _i8.MultiAddress account}) { - return _i7.Recovery(_i9.ClaimRecovery(account: account)); - } - - /// As the controller of a recoverable account, close an active recovery - /// process for your account. - /// - /// Payment: By calling this function, the recoverable account will receive - /// the recovery deposit `RecoveryDeposit` placed by the rescuer. - /// - /// The dispatch origin for this call must be _Signed_ and must be a - /// recoverable account with an active recovery process for it. - /// - /// Parameters: - /// - `rescuer`: The account trying to rescue this recoverable account. - _i7.Recovery closeRecovery({required _i8.MultiAddress rescuer}) { - return _i7.Recovery(_i9.CloseRecovery(rescuer: rescuer)); - } - - /// Remove the recovery process for your account. Recovered accounts are still accessible. - /// - /// NOTE: The user must make sure to call `close_recovery` on all active - /// recovery attempts before calling this function else it will fail. - /// - /// Payment: By calling this function the recoverable account will unreserve - /// their recovery configuration deposit. - /// (`ConfigDepositBase` + `FriendDepositFactor` * #_of_friends) - /// - /// The dispatch origin for this call must be _Signed_ and must be a - /// recoverable account (i.e. has a recovery configuration). - _i7.Recovery removeRecovery() { - return _i7.Recovery(_i9.RemoveRecovery()); - } - - /// Cancel the ability to use `as_recovered` for `account`. - /// - /// The dispatch origin for this call must be _Signed_ and registered to - /// be able to make calls on behalf of the recovered account. - /// - /// Parameters: - /// - `account`: The recovered account you are able to call on-behalf-of. - _i7.Recovery cancelRecovered({required _i8.MultiAddress account}) { - return _i7.Recovery(_i9.CancelRecovered(account: account)); - } - - /// Poke deposits for recovery configurations and / or active recoveries. - /// - /// This can be used by accounts to possibly lower their locked amount. - /// - /// The dispatch origin for this call must be _Signed_. - /// - /// Parameters: - /// - `maybe_account`: Optional recoverable account for which you have an active recovery - /// and want to adjust the deposit for the active recovery. - /// - /// This function checks both recovery configuration deposit and active recovery deposits - /// of the caller: - /// - If the caller has created a recovery configuration, checks and adjusts its deposit - /// - If the caller has initiated any active recoveries, and provides the account in - /// `maybe_account`, checks and adjusts those deposits - /// - /// If any deposit is updated, the difference will be reserved/unreserved from the caller's - /// account. - /// - /// The transaction is made free if any deposit is updated and paid otherwise. - /// - /// Emits `DepositPoked` if any deposit is updated. - /// Multiple events may be emitted in case both types of deposits are updated. - _i7.Recovery pokeDeposit({_i8.MultiAddress? maybeAccount}) { - return _i7.Recovery(_i9.PokeDeposit(maybeAccount: maybeAccount)); - } -} - -class Constants { - Constants(); - - /// The base amount of currency needed to reserve for creating a recovery configuration. - /// - /// This is held for an additional storage item whose value size is - /// `2 + sizeof(BlockNumber, Balance)` bytes. - final BigInt configDepositBase = BigInt.from(10000000000000); - - /// The amount of currency needed per additional user when creating a recovery - /// configuration. - /// - /// This is held for adding `sizeof(AccountId)` bytes more into a pre-existing storage - /// value. - final BigInt friendDepositFactor = BigInt.from(1000000000000); - - /// The maximum amount of friends allowed in a recovery configuration. - /// - /// NOTE: The threshold programmed in this Pallet uses u16, so it does - /// not really make sense to have a limit here greater than u16::MAX. - /// But also, that is a lot more than you should probably set this value - /// to anyway... - final int maxFriends = 9; - - /// The base amount of currency needed to reserve for starting a recovery. - /// - /// This is primarily held for deterring malicious recovery attempts, and should - /// have a value large enough that a bad actor would choose not to place this - /// deposit. It also acts to fund additional storage item whose value size is - /// `sizeof(BlockNumber, Balance + T * AccountId)` bytes. Where T is a configurable - /// threshold. - final BigInt recoveryDeposit = BigInt.from(10000000000000); -} diff --git a/quantus_sdk/lib/generated/planck/pallets/reversible_transfers.dart b/quantus_sdk/lib/generated/planck/pallets/reversible_transfers.dart index be65a88ca..f1f4fd9e1 100644 --- a/quantus_sdk/lib/generated/planck/pallets/reversible_transfers.dart +++ b/quantus_sdk/lib/generated/planck/pallets/reversible_transfers.dart @@ -301,6 +301,15 @@ class Txs { /// /// - `tx_id`: The unique identifier of the pending transfer to execute. /// + /// Execution uses `transfer_allow_death` so a sender who spent their leftover + /// free balance during the delay still completes. A failed inner transfer (e.g. + /// dest overflow, or `amount < ED` to a new account) does not fail this + /// extrinsic: the hold is already released and the pending transfer is already + /// removed. Propagating that error would roll back those writes (FRAME + /// dispatchables are transactional) while Scheduler terminally drops the named + /// task, freezing the funds with no retry. The inner result is still recorded on + /// [`Event::TransactionExecuted`]. + /// /// # Errors /// /// - [`InvalidSchedulerOrigin`](Error::InvalidSchedulerOrigin): Called by an account other diff --git a/quantus_sdk/lib/generated/planck/pallets/system.dart b/quantus_sdk/lib/generated/planck/pallets/system.dart index e015f1dea..1186a228c 100644 --- a/quantus_sdk/lib/generated/planck/pallets/system.dart +++ b/quantus_sdk/lib/generated/planck/pallets/system.dart @@ -703,16 +703,16 @@ class Constants { /// Block & extrinsics weights: base values and limits. final _i19.BlockWeights blockWeights = _i19.BlockWeights( - baseBlock: _i13.Weight(refTime: BigInt.from(431614000), proofSize: BigInt.zero), + baseBlock: _i13.Weight(refTime: BigInt.from(710231000), proofSize: BigInt.zero), maxBlock: _i13.Weight( refTime: BigInt.from(6000000000000), proofSize: BigInt.parse('18446744073709551615', radix: 10), ), perClass: _i20.PerDispatchClass( normal: _i21.WeightsPerClass( - baseExtrinsic: _i13.Weight(refTime: BigInt.from(108157000), proofSize: BigInt.zero), + baseExtrinsic: _i13.Weight(refTime: BigInt.from(767297000), proofSize: BigInt.zero), maxExtrinsic: _i13.Weight( - refTime: BigInt.from(3899460229000), + refTime: BigInt.from(3898522472000), proofSize: BigInt.parse('11990383647911208550', radix: 10), ), maxTotal: _i13.Weight( @@ -722,9 +722,9 @@ class Constants { reserved: _i13.Weight(refTime: BigInt.zero, proofSize: BigInt.zero), ), operational: _i21.WeightsPerClass( - baseExtrinsic: _i13.Weight(refTime: BigInt.from(108157000), proofSize: BigInt.zero), + baseExtrinsic: _i13.Weight(refTime: BigInt.from(767297000), proofSize: BigInt.zero), maxExtrinsic: _i13.Weight( - refTime: BigInt.from(5399460229000), + refTime: BigInt.from(5398522472000), proofSize: BigInt.parse('16602069666338596454', radix: 10), ), maxTotal: _i13.Weight( @@ -737,7 +737,7 @@ class Constants { ), ), mandatory: _i21.WeightsPerClass( - baseExtrinsic: _i13.Weight(refTime: BigInt.from(108157000), proofSize: BigInt.zero), + baseExtrinsic: _i13.Weight(refTime: BigInt.from(767297000), proofSize: BigInt.zero), maxExtrinsic: null, maxTotal: null, reserved: null, @@ -764,7 +764,7 @@ class Constants { specName: 'quantus-runtime', implName: 'quantus-runtime', authoringVersion: 1, - specVersion: 146, + specVersion: 147, implVersion: 1, apis: [ _i9.Tuple2, int>([223, 106, 203, 104, 153, 7, 96, 155], 5), @@ -780,7 +780,7 @@ class Constants { _i9.Tuple2, int>([243, 255, 20, 213, 171, 82, 112, 89], 3), _i9.Tuple2, int>([251, 197, 119, 185, 215, 71, 239, 214], 1), ], - transactionVersion: 5, + transactionVersion: 6, systemVersion: 1, ); diff --git a/quantus_sdk/lib/generated/planck/pallets/tech_referenda.dart b/quantus_sdk/lib/generated/planck/pallets/tech_referenda.dart index 1b9fb273a..d1fa472b0 100644 --- a/quantus_sdk/lib/generated/planck/pallets/tech_referenda.dart +++ b/quantus_sdk/lib/generated/planck/pallets/tech_referenda.dart @@ -463,7 +463,7 @@ class Constants { _i5.Tuple2( 0, _i14.TrackDetails( - name: 'tech_collective_members', + name: 'tech_collective_members', maxDeciding: 1, decisionDeposit: BigInt.from(1000000000000000), preparePeriod: 600, diff --git a/quantus_sdk/lib/generated/planck/pallets/vesting.dart b/quantus_sdk/lib/generated/planck/pallets/vesting.dart index 1442a9651..4a07dbfd0 100644 --- a/quantus_sdk/lib/generated/planck/pallets/vesting.dart +++ b/quantus_sdk/lib/generated/planck/pallets/vesting.dart @@ -84,7 +84,9 @@ class Txs { /// Pay the largest valid claim on `schedule_id` to its beneficiary. Payouts are /// rounded down to [`Config::PayoutQuantum`], must meet [`Config::MinimumPayout`], /// and reserve at least one minimum-sized final claim unless the schedule is fully - /// vested. + /// vested. Non-final payouts are further rounded down to + /// [`NON_FINAL_PAYOUT_QUANTA`] leaf quanta; the leftover stays on the schedule + /// until a later claim or the exact final payout. /// /// Permissionless: any signed account may call this for any schedule; the payout /// always goes to the stored beneficiary. This is the only claim path for @@ -107,19 +109,21 @@ class Txs { ); } - /// End a schedule early: the still-unpaid vested part (rounded down to a - /// [`Config::PayoutQuantum`] multiple) goes to the beneficiary, everything else - /// this schedule still holds — the unvested remainder plus any sub-quantum - /// vested dust — returns to the treasury, and the schedule is removed. The - /// treasury is signature-controlled and needs no wormhole leaf, so dust is safe - /// there but would be stranded on a keyless beneficiary. A non-zero beneficiary - /// payout below [`Config::MinimumPayout`] is rejected without ending the schedule. + /// End a schedule early: the still-unpaid vested part (rounded to the nearest + /// [`Config::PayoutQuantum`]) goes to the beneficiary if it meets + /// [`Config::MinimumPayout`]; otherwise that sliver is refunded with the + /// unvested remainder. The treasury is signature-controlled and needs no + /// wormhole leaf, so the refund is not quantized and never blocks ending. _i6.Vesting endSchedule({required BigInt scheduleId}) { return _i6.Vesting(_i7.EndSchedule(scheduleId: scheduleId)); } - /// Settle any payout a permissionless claim could currently force, then change the - /// beneficiary. This makes retargeting independent of claim transaction ordering. + /// Change the schedule's beneficiary without paying anything out. A retarget + /// replaces the wallet of the *same* grantee (lost-key remedy): the old address + /// may be lost or stolen, so settling it would burn funds or pay the thief. + /// Everything vested but unclaimed stays on the schedule and goes to the new + /// wallet at its next claim. (A permissionless claim landing before the + /// retarget still pays the old address, so rotate promptly.) _i6.Vesting retargetSchedule({required BigInt scheduleId, required _i8.AccountId32 newBeneficiary}) { return _i6.Vesting(_i7.RetargetSchedule(scheduleId: scheduleId, newBeneficiary: newBeneficiary)); } diff --git a/quantus_sdk/lib/generated/planck/pallets/zk_tree.dart b/quantus_sdk/lib/generated/planck/pallets/zk_tree.dart index 518803dd1..cfcaea659 100644 --- a/quantus_sdk/lib/generated/planck/pallets/zk_tree.dart +++ b/quantus_sdk/lib/generated/planck/pallets/zk_tree.dart @@ -46,6 +46,12 @@ class Queries { valueCodec: _i3.U8ArrayCodec(32), ); + final _i1.StorageValue _unprocessedLeaves = const _i1.StorageValue( + prefix: 'ZkTree', + storage: 'UnprocessedLeaves', + valueCodec: _i3.U64Codec.codec, + ); + /// Leaf data stored by index. _i5.Future<_i2.ZkLeaf?> leaves(BigInt key1, {_i1.BlockHash? at}) async { final hashedKey = _leaves.hashedKeyFor(key1); @@ -89,6 +95,10 @@ class Queries { } /// Current root hash of the tree. + /// + /// Covers exactly the first `LeafCount - UnprocessedLeaves` leaves: root + /// recomputation is batched once per block in `on_finalize`, so during block + /// execution this is the root as of the end of the previous block. _i5.Future> root({_i1.BlockHash? at}) async { final hashedKey = _root.hashedKey(); final bytes = await __api.getStorage(hashedKey, at: at); @@ -98,6 +108,17 @@ class Queries { return List.filled(32, 0, growable: false); /* Default */ } + /// Number of trailing leaves appended this block but not yet folded into + /// `Nodes`/`Root`. Always drained back to 0 by `on_finalize`. + _i5.Future unprocessedLeaves({_i1.BlockHash? at}) async { + final hashedKey = _unprocessedLeaves.hashedKey(); + final bytes = await __api.getStorage(hashedKey, at: at); + if (bytes != null) { + return _unprocessedLeaves.decodeValue(bytes); + } + return BigInt.zero; /* Default */ + } + /// Leaf data stored by index. _i5.Future> multiLeaves(List keys, {_i1.BlockHash? at}) async { final hashedKeys = keys.map((key) => _leaves.hashedKeyFor(key)).toList(); @@ -150,6 +171,12 @@ class Queries { return hashedKey; } + /// Returns the storage key for `unprocessedLeaves`. + _i6.Uint8List unprocessedLeavesKey() { + final hashedKey = _unprocessedLeaves.hashedKey(); + return hashedKey; + } + /// Returns the storage map key prefix for `leaves`. _i6.Uint8List leavesMapPrefix() { final hashedKey = _leaves.mapPrefix(); diff --git a/quantus_sdk/lib/generated/planck/planck.dart b/quantus_sdk/lib/generated/planck/planck.dart index 36a79df35..40de0c958 100644 --- a/quantus_sdk/lib/generated/planck/planck.dart +++ b/quantus_sdk/lib/generated/planck/planck.dart @@ -1,14 +1,13 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i20; +import 'dart:async' as _i19; import 'package:polkadart/polkadart.dart' as _i1; import 'pallets/balances.dart' as _i4; import 'pallets/mining_rewards.dart' as _i7; -import 'pallets/multisig.dart' as _i15; +import 'pallets/multisig.dart' as _i14; import 'pallets/preimage.dart' as _i8; import 'pallets/q_po_w.dart' as _i6; -import 'pallets/recovery.dart' as _i14; import 'pallets/reversible_transfers.dart' as _i10; import 'pallets/scheduler.dart' as _i9; import 'pallets/system.dart' as _i2; @@ -17,10 +16,10 @@ import 'pallets/tech_referenda.dart' as _i12; import 'pallets/timestamp.dart' as _i3; import 'pallets/transaction_payment.dart' as _i5; import 'pallets/treasury_pallet.dart' as _i13; -import 'pallets/utility.dart' as _i19; -import 'pallets/vesting.dart' as _i18; -import 'pallets/wormhole.dart' as _i16; -import 'pallets/zk_tree.dart' as _i17; +import 'pallets/utility.dart' as _i18; +import 'pallets/vesting.dart' as _i17; +import 'pallets/wormhole.dart' as _i15; +import 'pallets/zk_tree.dart' as _i16; class Queries { Queries(_i1.StateApi api) @@ -36,11 +35,10 @@ class Queries { techCollective = _i11.Queries(api), techReferenda = _i12.Queries(api), treasuryPallet = _i13.Queries(api), - recovery = _i14.Queries(api), - multisig = _i15.Queries(api), - wormhole = _i16.Queries(api), - zkTree = _i17.Queries(api), - vesting = _i18.Queries(api); + multisig = _i14.Queries(api), + wormhole = _i15.Queries(api), + zkTree = _i16.Queries(api), + vesting = _i17.Queries(api); final _i2.Queries system; @@ -66,15 +64,13 @@ class Queries { final _i13.Queries treasuryPallet; - final _i14.Queries recovery; + final _i14.Queries multisig; - final _i15.Queries multisig; + final _i15.Queries wormhole; - final _i16.Queries wormhole; + final _i16.Queries zkTree; - final _i17.Queries zkTree; - - final _i18.Queries vesting; + final _i17.Queries vesting; } class Extrinsics { @@ -88,7 +84,7 @@ class Extrinsics { final _i8.Txs preimage = _i8.Txs(); - final _i19.Txs utility = _i19.Txs(); + final _i18.Txs utility = _i18.Txs(); final _i10.Txs reversibleTransfers = _i10.Txs(); @@ -98,13 +94,11 @@ class Extrinsics { final _i13.Txs treasuryPallet = _i13.Txs(); - final _i14.Txs recovery = _i14.Txs(); - - final _i15.Txs multisig = _i15.Txs(); + final _i14.Txs multisig = _i14.Txs(); - final _i16.Txs wormhole = _i16.Txs(); + final _i15.Txs wormhole = _i15.Txs(); - final _i18.Txs vesting = _i18.Txs(); + final _i17.Txs vesting = _i17.Txs(); } class Constants { @@ -124,19 +118,17 @@ class Constants { final _i9.Constants scheduler = _i9.Constants(); - final _i19.Constants utility = _i19.Constants(); + final _i18.Constants utility = _i18.Constants(); final _i10.Constants reversibleTransfers = _i10.Constants(); final _i12.Constants techReferenda = _i12.Constants(); - final _i14.Constants recovery = _i14.Constants(); - - final _i15.Constants multisig = _i15.Constants(); + final _i14.Constants multisig = _i14.Constants(); - final _i16.Constants wormhole = _i16.Constants(); + final _i15.Constants wormhole = _i15.Constants(); - final _i18.Constants vesting = _i18.Constants(); + final _i17.Constants vesting = _i17.Constants(); } class Rpc { @@ -190,11 +182,11 @@ class Planck { final Registry registry; - _i20.Future connect() async { + _i19.Future connect() async { return await _provider.connect(); } - _i20.Future disconnect() async { + _i19.Future disconnect() async { return await _provider.disconnect(); } } diff --git a/quantus_sdk/lib/generated/planck/types/frame_metadata_hash_extension/mode.dart b/quantus_sdk/lib/generated/planck/types/frame_metadata_hash_extension/mode.dart index 2abf6f498..5ec6e0eb1 100644 --- a/quantus_sdk/lib/generated/planck/types/frame_metadata_hash_extension/mode.dart +++ b/quantus_sdk/lib/generated/planck/types/frame_metadata_hash_extension/mode.dart @@ -20,6 +20,7 @@ enum Mode { static const $ModeCodec codec = $ModeCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/dispatch_class.dart b/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/dispatch_class.dart index a27bebbee..e4f8be5b0 100644 --- a/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/dispatch_class.dart +++ b/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/dispatch_class.dart @@ -21,6 +21,7 @@ enum DispatchClass { static const $DispatchClassCodec codec = $DispatchClassCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/pays.dart b/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/pays.dart index 00b962e6c..053946498 100644 --- a/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/pays.dart +++ b/quantus_sdk/lib/generated/planck/types/frame_support/dispatch/pays.dart @@ -20,6 +20,7 @@ enum Pays { static const $PaysCodec codec = $PaysCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/frame_support/traits/tokens/misc/balance_status.dart b/quantus_sdk/lib/generated/planck/types/frame_support/traits/tokens/misc/balance_status.dart index 6d0c1a3d3..5f8304909 100644 --- a/quantus_sdk/lib/generated/planck/types/frame_support/traits/tokens/misc/balance_status.dart +++ b/quantus_sdk/lib/generated/planck/types/frame_support/traits/tokens/misc/balance_status.dart @@ -20,6 +20,7 @@ enum BalanceStatus { static const $BalanceStatusCodec codec = $BalanceStatusCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/frame_system/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/frame_system/pallet/error.dart index a8ab4ccc5..62c71122f 100644 --- a/quantus_sdk/lib/generated/planck/types/frame_system/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/frame_system/pallet/error.dart @@ -52,6 +52,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/error.dart index 594f86df9..e46b800f9 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/error.dart @@ -48,6 +48,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/unexpected_kind.dart b/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/unexpected_kind.dart index 7fd4a5cdd..98c84a751 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/unexpected_kind.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_balances/pallet/unexpected_kind.dart @@ -20,6 +20,7 @@ enum UnexpectedKind { static const $UnexpectedKindCodec codec = $UnexpectedKindCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_balances/types/reasons.dart b/quantus_sdk/lib/generated/planck/types/pallet_balances/types/reasons.dart index 8ed8b2934..6fbab9475 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_balances/types/reasons.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_balances/types/reasons.dart @@ -21,6 +21,7 @@ enum Reasons { static const $ReasonsCodec codec = $ReasonsCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/call.dart b/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/call.dart index 3b95493ea..ee8d65a4b 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/call.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/call.dart @@ -2,8 +2,9 @@ import 'dart:typed_data' as _i2; import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i4; +import 'package:quiver/collection.dart' as _i5; +import '../../quantus_runtime/runtime_call.dart' as _i4; import '../../sp_core/crypto/account_id32.dart' as _i3; /// Contains a variant per dispatchable extrinsic that this pallet has. @@ -62,8 +63,8 @@ class $Call { return ClaimDeposits(multisigAddress: multisigAddress); } - Execute execute({required _i3.AccountId32 multisigAddress, required int proposalId}) { - return Execute(multisigAddress: multisigAddress, proposalId: proposalId); + Execute execute({required _i3.AccountId32 multisigAddress, required int proposalId, required _i4.RuntimeCall call}) { + return Execute(multisigAddress: multisigAddress, proposalId: proposalId, call: call); } } @@ -155,7 +156,8 @@ class $CallCodec with _i1.Codec { /// The multisig address is deterministically derived from: /// hash(pallet_id || sorted_signers || threshold || nonce) /// -/// Signers are automatically sorted before hashing, so order doesn't matter. +/// Signers are sorted before hashing, so order doesn't matter. +/// Duplicate accounts are rejected. /// /// Economic costs: /// - MultisigFee: burned immediately (spam prevention) @@ -207,7 +209,7 @@ class CreateMultisig extends Call { bool operator ==(Object other) => identical(this, other) || other is CreateMultisig && - _i4.listsEqual(other.signers, signers) && + _i5.listsEqual(other.signers, signers) && other.threshold == threshold && other.nonce == nonce; @@ -278,8 +280,8 @@ class Propose extends Call { bool operator ==(Object other) => identical(this, other) || other is Propose && - _i4.listsEqual(other.multisigAddress, multisigAddress) && - _i4.listsEqual(other.call, call) && + _i5.listsEqual(other.multisigAddress, multisigAddress) && + _i5.listsEqual(other.call, call) && other.expiry == expiry; @override @@ -347,9 +349,9 @@ class Approve extends Call { bool operator ==(Object other) => identical(this, other) || other is Approve && - _i4.listsEqual(other.multisigAddress, multisigAddress) && + _i5.listsEqual(other.multisigAddress, multisigAddress) && other.proposalId == proposalId && - _i4.listsEqual(other.call, call); + _i5.listsEqual(other.call, call); @override int get hashCode => Object.hash(multisigAddress, proposalId, call); @@ -397,7 +399,7 @@ class Cancel extends Call { @override bool operator ==(Object other) => identical(this, other) || - other is Cancel && _i4.listsEqual(other.multisigAddress, multisigAddress) && other.proposalId == proposalId; + other is Cancel && _i5.listsEqual(other.multisigAddress, multisigAddress) && other.proposalId == proposalId; @override int get hashCode => Object.hash(multisigAddress, proposalId); @@ -452,7 +454,7 @@ class RemoveExpired extends Call { bool operator ==(Object other) => identical(this, other) || other is RemoveExpired && - _i4.listsEqual(other.multisigAddress, multisigAddress) && + _i5.listsEqual(other.multisigAddress, multisigAddress) && other.proposalId == proposalId; @override @@ -497,7 +499,7 @@ class ClaimDeposits extends Call { @override bool operator ==(Object other) => - identical(this, other) || other is ClaimDeposits && _i4.listsEqual(other.multisigAddress, multisigAddress); + identical(this, other) || other is ClaimDeposits && _i5.listsEqual(other.multisigAddress, multisigAddress); @override int get hashCode => multisigAddress.hashCode; @@ -508,25 +510,37 @@ class ClaimDeposits extends Call { /// Can be called by any signer of the multisig once the proposal has reached /// the approval threshold (status = Approved). The proposal must not be expired. /// +/// The executor resubmits the proposal's inner call; execution proceeds only +/// if it is byte-equal to the payload stored at `proposal_id` — the same +/// binding `approve` enforces. This serves two purposes: +/// - **Clearsigning:** the executor's (hardware) wallet displays and signs the actual call +/// being dispatched, not an opaque proposal id. +/// - **Self-describing weight:** the executing extrinsic carries the inner call, so its +/// declared weight carries the inner call's own declared weight (refunded to actuals +/// post-dispatch) instead of reserving a flat `MaxInnerCallWeight`, and runtime +/// transaction extensions can inspect the inner call and price its side effects +/// (account-reap cleanup, transfer-proof recording) exactly as they do for directly +/// submitted calls. Nothing about the dispatch is invisible to pre-dispatch admission or +/// fees. (Only the bookkeeping term is reserved at `MaxCallSize`, since the stored bytes' +/// length is unknown pre-dispatch; the unused remainder is refunded.) +/// /// On execution: -/// - The call is decoded and dispatched as the multisig account +/// - The call is dispatched as the multisig account /// - Proposal is removed from storage /// - Deposit is returned to the proposer /// /// Parameters: /// - `multisig_address`: The multisig account /// - `proposal_id`: ID (nonce) of the proposal to execute -/// -/// Note: The weight charged includes both multisig bookkeeping and MaxInnerCallWeight. -/// Actual weight is refunded based on the inner call's post-dispatch info. -/// The inner call's weight is validated against MaxInnerCallWeight at propose time. +/// - `call`: The proposal's inner call, byte-equal to the stored payload class Execute extends Call { - const Execute({required this.multisigAddress, required this.proposalId}); + const Execute({required this.multisigAddress, required this.proposalId, required this.call}); factory Execute._decode(_i1.Input input) { return Execute( multisigAddress: const _i1.U8ArrayCodec(32).decode(input), proposalId: _i1.U32Codec.codec.decode(input), + call: _i4.RuntimeCall.codec.decode(input), ); } @@ -536,15 +550,19 @@ class Execute extends Call { /// u32 final int proposalId; + /// Box<::RuntimeCall> + final _i4.RuntimeCall call; + @override Map> toJson() => { - 'execute': {'multisigAddress': multisigAddress.toList(), 'proposalId': proposalId}, + 'execute': {'multisigAddress': multisigAddress.toList(), 'proposalId': proposalId, 'call': call.toJson()}, }; int _sizeHint() { int size = 1; size = size + const _i3.AccountId32Codec().sizeHint(multisigAddress); size = size + _i1.U32Codec.codec.sizeHint(proposalId); + size = size + _i4.RuntimeCall.codec.sizeHint(call); return size; } @@ -552,13 +570,17 @@ class Execute extends Call { _i1.U8Codec.codec.encodeTo(6, output); const _i1.U8ArrayCodec(32).encodeTo(multisigAddress, output); _i1.U32Codec.codec.encodeTo(proposalId, output); + _i4.RuntimeCall.codec.encodeTo(call, output); } @override bool operator ==(Object other) => identical(this, other) || - other is Execute && _i4.listsEqual(other.multisigAddress, multisigAddress) && other.proposalId == proposalId; + other is Execute && + _i5.listsEqual(other.multisigAddress, multisigAddress) && + other.proposalId == proposalId && + other.call == call; @override - int get hashCode => Object.hash(multisigAddress, proposalId); + int get hashCode => Object.hash(multisigAddress, proposalId, call); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/error.dart index 7bbd46de3..a4ae65acc 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_multisig/pallet/error.dart @@ -82,7 +82,10 @@ enum Error { callWeightExceedsLimit('CallWeightExceedsLimit', 24), /// Provided call does not match the stored proposal payload - callMismatch('CallMismatch', 25); + callMismatch('CallMismatch', 25), + + /// Signer list contains the same account more than once + duplicateSigners('DuplicateSigners', 26); const Error(this.variantName, this.codecIndex); @@ -97,6 +100,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } @@ -161,6 +165,8 @@ class $ErrorCodec with _i1.Codec { return Error.callWeightExceedsLimit; case 25: return Error.callMismatch; + case 26: + return Error.duplicateSigners; default: throw Exception('Error: Invalid variant index: "$index"'); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_multisig/proposal_status.dart b/quantus_sdk/lib/generated/planck/types/pallet_multisig/proposal_status.dart index efd07c85a..6019a34cc 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_multisig/proposal_status.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_multisig/proposal_status.dart @@ -20,6 +20,7 @@ enum ProposalStatus { static const $ProposalStatusCodec codec = $ProposalStatusCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/error.dart index 596aaa89c..74a1a50f0 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/error.dart @@ -42,6 +42,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/hold_reason.dart b/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/hold_reason.dart index ed80e5ab5..6bcca1d89 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/hold_reason.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_preimage/pallet/hold_reason.dart @@ -19,6 +19,7 @@ enum HoldReason { static const $HoldReasonCodec codec = $HoldReasonCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_ranked_collective/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_ranked_collective/pallet/error.dart index 3906b1e7b..a0939c339 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_ranked_collective/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_ranked_collective/pallet/error.dart @@ -51,6 +51,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/active_recovery.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/active_recovery.dart deleted file mode 100644 index 2df5af34d..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/active_recovery.dart +++ /dev/null @@ -1,76 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i3; - -import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i4; - -import '../sp_core/crypto/account_id32.dart' as _i2; - -class ActiveRecovery { - const ActiveRecovery({required this.created, required this.deposit, required this.friends}); - - factory ActiveRecovery.decode(_i1.Input input) { - return codec.decode(input); - } - - /// BlockNumber - final int created; - - /// Balance - final BigInt deposit; - - /// Friends - final List<_i2.AccountId32> friends; - - static const $ActiveRecoveryCodec codec = $ActiveRecoveryCodec(); - - _i3.Uint8List encode() { - return codec.encode(this); - } - - Map toJson() => { - 'created': created, - 'deposit': deposit, - 'friends': friends.map((value) => value.toList()).toList(), - }; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is ActiveRecovery && - other.created == created && - other.deposit == deposit && - _i4.listsEqual(other.friends, friends); - - @override - int get hashCode => Object.hash(created, deposit, friends); -} - -class $ActiveRecoveryCodec with _i1.Codec { - const $ActiveRecoveryCodec(); - - @override - void encodeTo(ActiveRecovery obj, _i1.Output output) { - _i1.U32Codec.codec.encodeTo(obj.created, output); - _i1.U128Codec.codec.encodeTo(obj.deposit, output); - const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).encodeTo(obj.friends, output); - } - - @override - ActiveRecovery decode(_i1.Input input) { - return ActiveRecovery( - created: _i1.U32Codec.codec.decode(input), - deposit: _i1.U128Codec.codec.decode(input), - friends: const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).decode(input), - ); - } - - @override - int sizeHint(ActiveRecovery obj) { - int size = 0; - size = size + _i1.U32Codec.codec.sizeHint(obj.created); - size = size + _i1.U128Codec.codec.sizeHint(obj.deposit); - size = size + const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).sizeHint(obj.friends); - return size; - } -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/deposit_kind.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/deposit_kind.dart deleted file mode 100644 index 8e0a6d10e..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/deposit_kind.dart +++ /dev/null @@ -1,135 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i2; - -import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i4; - -import '../sp_core/crypto/account_id32.dart' as _i3; - -abstract class DepositKind { - const DepositKind(); - - factory DepositKind.decode(_i1.Input input) { - return codec.decode(input); - } - - static const $DepositKindCodec codec = $DepositKindCodec(); - - static const $DepositKind values = $DepositKind(); - - _i2.Uint8List encode() { - final output = _i1.ByteOutput(codec.sizeHint(this)); - codec.encodeTo(this, output); - return output.toBytes(); - } - - int sizeHint() { - return codec.sizeHint(this); - } - - Map toJson(); -} - -class $DepositKind { - const $DepositKind(); - - RecoveryConfig recoveryConfig() { - return RecoveryConfig(); - } - - ActiveRecoveryFor activeRecoveryFor(_i3.AccountId32 value0) { - return ActiveRecoveryFor(value0); - } -} - -class $DepositKindCodec with _i1.Codec { - const $DepositKindCodec(); - - @override - DepositKind decode(_i1.Input input) { - final index = _i1.U8Codec.codec.decode(input); - switch (index) { - case 0: - return const RecoveryConfig(); - case 1: - return ActiveRecoveryFor._decode(input); - default: - throw Exception('DepositKind: Invalid variant index: "$index"'); - } - } - - @override - void encodeTo(DepositKind value, _i1.Output output) { - switch (value.runtimeType) { - case RecoveryConfig: - (value as RecoveryConfig).encodeTo(output); - break; - case ActiveRecoveryFor: - (value as ActiveRecoveryFor).encodeTo(output); - break; - default: - throw Exception('DepositKind: Unsupported "$value" of type "${value.runtimeType}"'); - } - } - - @override - int sizeHint(DepositKind value) { - switch (value.runtimeType) { - case RecoveryConfig: - return 1; - case ActiveRecoveryFor: - return (value as ActiveRecoveryFor)._sizeHint(); - default: - throw Exception('DepositKind: Unsupported "$value" of type "${value.runtimeType}"'); - } - } -} - -class RecoveryConfig extends DepositKind { - const RecoveryConfig(); - - @override - Map toJson() => {'RecoveryConfig': null}; - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(0, output); - } - - @override - bool operator ==(Object other) => other is RecoveryConfig; - - @override - int get hashCode => runtimeType.hashCode; -} - -class ActiveRecoveryFor extends DepositKind { - const ActiveRecoveryFor(this.value0); - - factory ActiveRecoveryFor._decode(_i1.Input input) { - return ActiveRecoveryFor(const _i1.U8ArrayCodec(32).decode(input)); - } - - /// ::AccountId - final _i3.AccountId32 value0; - - @override - Map> toJson() => {'ActiveRecoveryFor': value0.toList()}; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(value0); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(1, output); - const _i1.U8ArrayCodec(32).encodeTo(value0, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is ActiveRecoveryFor && _i4.listsEqual(other.value0, value0); - - @override - int get hashCode => value0.hashCode; -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/call.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/call.dart deleted file mode 100644 index 6aa962cbd..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/call.dart +++ /dev/null @@ -1,653 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i2; - -import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i6; - -import '../../quantus_runtime/runtime_call.dart' as _i4; -import '../../sp_core/crypto/account_id32.dart' as _i5; -import '../../sp_runtime/multiaddress/multi_address.dart' as _i3; - -/// Contains a variant per dispatchable extrinsic that this pallet has. -abstract class Call { - const Call(); - - factory Call.decode(_i1.Input input) { - return codec.decode(input); - } - - static const $CallCodec codec = $CallCodec(); - - static const $Call values = $Call(); - - _i2.Uint8List encode() { - final output = _i1.ByteOutput(codec.sizeHint(this)); - codec.encodeTo(this, output); - return output.toBytes(); - } - - int sizeHint() { - return codec.sizeHint(this); - } - - Map toJson(); -} - -class $Call { - const $Call(); - - AsRecovered asRecovered({required _i3.MultiAddress account, required _i4.RuntimeCall call}) { - return AsRecovered(account: account, call: call); - } - - SetRecovered setRecovered({required _i3.MultiAddress lost, required _i3.MultiAddress rescuer}) { - return SetRecovered(lost: lost, rescuer: rescuer); - } - - CreateRecovery createRecovery({ - required List<_i5.AccountId32> friends, - required int threshold, - required int delayPeriod, - }) { - return CreateRecovery(friends: friends, threshold: threshold, delayPeriod: delayPeriod); - } - - InitiateRecovery initiateRecovery({required _i3.MultiAddress account}) { - return InitiateRecovery(account: account); - } - - VouchRecovery vouchRecovery({required _i3.MultiAddress lost, required _i3.MultiAddress rescuer}) { - return VouchRecovery(lost: lost, rescuer: rescuer); - } - - ClaimRecovery claimRecovery({required _i3.MultiAddress account}) { - return ClaimRecovery(account: account); - } - - CloseRecovery closeRecovery({required _i3.MultiAddress rescuer}) { - return CloseRecovery(rescuer: rescuer); - } - - RemoveRecovery removeRecovery() { - return RemoveRecovery(); - } - - CancelRecovered cancelRecovered({required _i3.MultiAddress account}) { - return CancelRecovered(account: account); - } - - PokeDeposit pokeDeposit({_i3.MultiAddress? maybeAccount}) { - return PokeDeposit(maybeAccount: maybeAccount); - } -} - -class $CallCodec with _i1.Codec { - const $CallCodec(); - - @override - Call decode(_i1.Input input) { - final index = _i1.U8Codec.codec.decode(input); - switch (index) { - case 0: - return AsRecovered._decode(input); - case 1: - return SetRecovered._decode(input); - case 2: - return CreateRecovery._decode(input); - case 3: - return InitiateRecovery._decode(input); - case 4: - return VouchRecovery._decode(input); - case 5: - return ClaimRecovery._decode(input); - case 6: - return CloseRecovery._decode(input); - case 7: - return const RemoveRecovery(); - case 8: - return CancelRecovered._decode(input); - case 9: - return PokeDeposit._decode(input); - default: - throw Exception('Call: Invalid variant index: "$index"'); - } - } - - @override - void encodeTo(Call value, _i1.Output output) { - switch (value.runtimeType) { - case AsRecovered: - (value as AsRecovered).encodeTo(output); - break; - case SetRecovered: - (value as SetRecovered).encodeTo(output); - break; - case CreateRecovery: - (value as CreateRecovery).encodeTo(output); - break; - case InitiateRecovery: - (value as InitiateRecovery).encodeTo(output); - break; - case VouchRecovery: - (value as VouchRecovery).encodeTo(output); - break; - case ClaimRecovery: - (value as ClaimRecovery).encodeTo(output); - break; - case CloseRecovery: - (value as CloseRecovery).encodeTo(output); - break; - case RemoveRecovery: - (value as RemoveRecovery).encodeTo(output); - break; - case CancelRecovered: - (value as CancelRecovered).encodeTo(output); - break; - case PokeDeposit: - (value as PokeDeposit).encodeTo(output); - break; - default: - throw Exception('Call: Unsupported "$value" of type "${value.runtimeType}"'); - } - } - - @override - int sizeHint(Call value) { - switch (value.runtimeType) { - case AsRecovered: - return (value as AsRecovered)._sizeHint(); - case SetRecovered: - return (value as SetRecovered)._sizeHint(); - case CreateRecovery: - return (value as CreateRecovery)._sizeHint(); - case InitiateRecovery: - return (value as InitiateRecovery)._sizeHint(); - case VouchRecovery: - return (value as VouchRecovery)._sizeHint(); - case ClaimRecovery: - return (value as ClaimRecovery)._sizeHint(); - case CloseRecovery: - return (value as CloseRecovery)._sizeHint(); - case RemoveRecovery: - return 1; - case CancelRecovered: - return (value as CancelRecovered)._sizeHint(); - case PokeDeposit: - return (value as PokeDeposit)._sizeHint(); - default: - throw Exception('Call: Unsupported "$value" of type "${value.runtimeType}"'); - } - } -} - -/// Send a call through a recovered account. -/// -/// The dispatch origin for this call must be _Signed_ and registered to -/// be able to make calls on behalf of the recovered account. -/// -/// Parameters: -/// - `account`: The recovered account you want to make a call on-behalf-of. -/// - `call`: The call you want to make with the recovered account. -class AsRecovered extends Call { - const AsRecovered({required this.account, required this.call}); - - factory AsRecovered._decode(_i1.Input input) { - return AsRecovered(account: _i3.MultiAddress.codec.decode(input), call: _i4.RuntimeCall.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress account; - - /// Box<::RuntimeCall> - final _i4.RuntimeCall call; - - @override - Map>> toJson() => { - 'as_recovered': {'account': account.toJson(), 'call': call.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(account); - size = size + _i4.RuntimeCall.codec.sizeHint(call); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(0, output); - _i3.MultiAddress.codec.encodeTo(account, output); - _i4.RuntimeCall.codec.encodeTo(call, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is AsRecovered && other.account == account && other.call == call; - - @override - int get hashCode => Object.hash(account, call); -} - -/// Allow ROOT to bypass the recovery process and set a rescuer account -/// for a lost account directly. -/// -/// The dispatch origin for this call must be _ROOT_. -/// -/// Parameters: -/// - `lost`: The "lost account" to be recovered. -/// - `rescuer`: The "rescuer account" which can call as the lost account. -class SetRecovered extends Call { - const SetRecovered({required this.lost, required this.rescuer}); - - factory SetRecovered._decode(_i1.Input input) { - return SetRecovered(lost: _i3.MultiAddress.codec.decode(input), rescuer: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress lost; - - /// AccountIdLookupOf - final _i3.MultiAddress rescuer; - - @override - Map>> toJson() => { - 'set_recovered': {'lost': lost.toJson(), 'rescuer': rescuer.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(lost); - size = size + _i3.MultiAddress.codec.sizeHint(rescuer); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(1, output); - _i3.MultiAddress.codec.encodeTo(lost, output); - _i3.MultiAddress.codec.encodeTo(rescuer, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is SetRecovered && other.lost == lost && other.rescuer == rescuer; - - @override - int get hashCode => Object.hash(lost, rescuer); -} - -/// Create a recovery configuration for your account. This makes your account recoverable. -/// -/// Payment: `ConfigDepositBase` + `FriendDepositFactor` * #_of_friends balance -/// will be reserved for storing the recovery configuration. This deposit is returned -/// in full when the user calls `remove_recovery`. -/// -/// The dispatch origin for this call must be _Signed_. -/// -/// Parameters: -/// - `friends`: A list of friends you trust to vouch for recovery attempts. Should be -/// ordered and contain no duplicate values. -/// - `threshold`: The number of friends that must vouch for a recovery attempt before the -/// account can be recovered. Should be less than or equal to the length of the list of -/// friends. -/// - `delay_period`: The number of blocks after a recovery attempt is initialized that -/// needs to pass before the account can be recovered. -class CreateRecovery extends Call { - const CreateRecovery({required this.friends, required this.threshold, required this.delayPeriod}); - - factory CreateRecovery._decode(_i1.Input input) { - return CreateRecovery( - friends: const _i1.SequenceCodec<_i5.AccountId32>(_i5.AccountId32Codec()).decode(input), - threshold: _i1.U16Codec.codec.decode(input), - delayPeriod: _i1.U32Codec.codec.decode(input), - ); - } - - /// Vec - final List<_i5.AccountId32> friends; - - /// u16 - final int threshold; - - /// BlockNumberFromProviderOf - final int delayPeriod; - - @override - Map> toJson() => { - 'create_recovery': { - 'friends': friends.map((value) => value.toList()).toList(), - 'threshold': threshold, - 'delayPeriod': delayPeriod, - }, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i1.SequenceCodec<_i5.AccountId32>(_i5.AccountId32Codec()).sizeHint(friends); - size = size + _i1.U16Codec.codec.sizeHint(threshold); - size = size + _i1.U32Codec.codec.sizeHint(delayPeriod); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(2, output); - const _i1.SequenceCodec<_i5.AccountId32>(_i5.AccountId32Codec()).encodeTo(friends, output); - _i1.U16Codec.codec.encodeTo(threshold, output); - _i1.U32Codec.codec.encodeTo(delayPeriod, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is CreateRecovery && - _i6.listsEqual(other.friends, friends) && - other.threshold == threshold && - other.delayPeriod == delayPeriod; - - @override - int get hashCode => Object.hash(friends, threshold, delayPeriod); -} - -/// Initiate the process for recovering a recoverable account. -/// -/// Payment: `RecoveryDeposit` balance will be reserved for initiating the -/// recovery process. This deposit will always be repatriated to the account -/// trying to be recovered. See `close_recovery`. -/// -/// The dispatch origin for this call must be _Signed_. -/// -/// Parameters: -/// - `account`: The lost account that you want to recover. This account needs to be -/// recoverable (i.e. have a recovery configuration). -class InitiateRecovery extends Call { - const InitiateRecovery({required this.account}); - - factory InitiateRecovery._decode(_i1.Input input) { - return InitiateRecovery(account: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress account; - - @override - Map>> toJson() => { - 'initiate_recovery': {'account': account.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(account); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(3, output); - _i3.MultiAddress.codec.encodeTo(account, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is InitiateRecovery && other.account == account; - - @override - int get hashCode => account.hashCode; -} - -/// Allow a "friend" of a recoverable account to vouch for an active recovery -/// process for that account. -/// -/// The dispatch origin for this call must be _Signed_ and must be a "friend" -/// for the recoverable account. -/// -/// Parameters: -/// - `lost`: The lost account that you want to recover. -/// - `rescuer`: The account trying to rescue the lost account that you want to vouch for. -/// -/// The combination of these two parameters must point to an active recovery -/// process. -class VouchRecovery extends Call { - const VouchRecovery({required this.lost, required this.rescuer}); - - factory VouchRecovery._decode(_i1.Input input) { - return VouchRecovery(lost: _i3.MultiAddress.codec.decode(input), rescuer: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress lost; - - /// AccountIdLookupOf - final _i3.MultiAddress rescuer; - - @override - Map>> toJson() => { - 'vouch_recovery': {'lost': lost.toJson(), 'rescuer': rescuer.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(lost); - size = size + _i3.MultiAddress.codec.sizeHint(rescuer); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(4, output); - _i3.MultiAddress.codec.encodeTo(lost, output); - _i3.MultiAddress.codec.encodeTo(rescuer, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is VouchRecovery && other.lost == lost && other.rescuer == rescuer; - - @override - int get hashCode => Object.hash(lost, rescuer); -} - -/// Allow a successful rescuer to claim their recovered account. -/// -/// The dispatch origin for this call must be _Signed_ and must be a "rescuer" -/// who has successfully completed the account recovery process: collected -/// `threshold` or more vouches, waited `delay_period` blocks since initiation. -/// -/// Parameters: -/// - `account`: The lost account that you want to claim has been successfully recovered by -/// you. -class ClaimRecovery extends Call { - const ClaimRecovery({required this.account}); - - factory ClaimRecovery._decode(_i1.Input input) { - return ClaimRecovery(account: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress account; - - @override - Map>> toJson() => { - 'claim_recovery': {'account': account.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(account); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(5, output); - _i3.MultiAddress.codec.encodeTo(account, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is ClaimRecovery && other.account == account; - - @override - int get hashCode => account.hashCode; -} - -/// As the controller of a recoverable account, close an active recovery -/// process for your account. -/// -/// Payment: By calling this function, the recoverable account will receive -/// the recovery deposit `RecoveryDeposit` placed by the rescuer. -/// -/// The dispatch origin for this call must be _Signed_ and must be a -/// recoverable account with an active recovery process for it. -/// -/// Parameters: -/// - `rescuer`: The account trying to rescue this recoverable account. -class CloseRecovery extends Call { - const CloseRecovery({required this.rescuer}); - - factory CloseRecovery._decode(_i1.Input input) { - return CloseRecovery(rescuer: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress rescuer; - - @override - Map>> toJson() => { - 'close_recovery': {'rescuer': rescuer.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(rescuer); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(6, output); - _i3.MultiAddress.codec.encodeTo(rescuer, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is CloseRecovery && other.rescuer == rescuer; - - @override - int get hashCode => rescuer.hashCode; -} - -/// Remove the recovery process for your account. Recovered accounts are still accessible. -/// -/// NOTE: The user must make sure to call `close_recovery` on all active -/// recovery attempts before calling this function else it will fail. -/// -/// Payment: By calling this function the recoverable account will unreserve -/// their recovery configuration deposit. -/// (`ConfigDepositBase` + `FriendDepositFactor` * #_of_friends) -/// -/// The dispatch origin for this call must be _Signed_ and must be a -/// recoverable account (i.e. has a recovery configuration). -class RemoveRecovery extends Call { - const RemoveRecovery(); - - @override - Map toJson() => {'remove_recovery': null}; - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(7, output); - } - - @override - bool operator ==(Object other) => other is RemoveRecovery; - - @override - int get hashCode => runtimeType.hashCode; -} - -/// Cancel the ability to use `as_recovered` for `account`. -/// -/// The dispatch origin for this call must be _Signed_ and registered to -/// be able to make calls on behalf of the recovered account. -/// -/// Parameters: -/// - `account`: The recovered account you are able to call on-behalf-of. -class CancelRecovered extends Call { - const CancelRecovered({required this.account}); - - factory CancelRecovered._decode(_i1.Input input) { - return CancelRecovered(account: _i3.MultiAddress.codec.decode(input)); - } - - /// AccountIdLookupOf - final _i3.MultiAddress account; - - @override - Map>> toJson() => { - 'cancel_recovered': {'account': account.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + _i3.MultiAddress.codec.sizeHint(account); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(8, output); - _i3.MultiAddress.codec.encodeTo(account, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is CancelRecovered && other.account == account; - - @override - int get hashCode => account.hashCode; -} - -/// Poke deposits for recovery configurations and / or active recoveries. -/// -/// This can be used by accounts to possibly lower their locked amount. -/// -/// The dispatch origin for this call must be _Signed_. -/// -/// Parameters: -/// - `maybe_account`: Optional recoverable account for which you have an active recovery -/// and want to adjust the deposit for the active recovery. -/// -/// This function checks both recovery configuration deposit and active recovery deposits -/// of the caller: -/// - If the caller has created a recovery configuration, checks and adjusts its deposit -/// - If the caller has initiated any active recoveries, and provides the account in -/// `maybe_account`, checks and adjusts those deposits -/// -/// If any deposit is updated, the difference will be reserved/unreserved from the caller's -/// account. -/// -/// The transaction is made free if any deposit is updated and paid otherwise. -/// -/// Emits `DepositPoked` if any deposit is updated. -/// Multiple events may be emitted in case both types of deposits are updated. -class PokeDeposit extends Call { - const PokeDeposit({this.maybeAccount}); - - factory PokeDeposit._decode(_i1.Input input) { - return PokeDeposit(maybeAccount: const _i1.OptionCodec<_i3.MultiAddress>(_i3.MultiAddress.codec).decode(input)); - } - - /// Option> - final _i3.MultiAddress? maybeAccount; - - @override - Map?>> toJson() => { - 'poke_deposit': {'maybeAccount': maybeAccount?.toJson()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i1.OptionCodec<_i3.MultiAddress>(_i3.MultiAddress.codec).sizeHint(maybeAccount); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(9, output); - const _i1.OptionCodec<_i3.MultiAddress>(_i3.MultiAddress.codec).encodeTo(maybeAccount, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is PokeDeposit && other.maybeAccount == maybeAccount; - - @override - int get hashCode => maybeAccount.hashCode; -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/error.dart deleted file mode 100644 index 92490ad94..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/error.dart +++ /dev/null @@ -1,127 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i2; - -import 'package:polkadart/scale_codec.dart' as _i1; - -/// The `Error` enum of this pallet. -enum Error { - /// User is not allowed to make a call on behalf of this account - notAllowed('NotAllowed', 0), - - /// Call is not allowed for a high-security account - callNotAllowedForHighSecurity('CallNotAllowedForHighSecurity', 1), - - /// Threshold must be greater than zero - zeroThreshold('ZeroThreshold', 2), - - /// Friends list must be greater than zero and threshold - notEnoughFriends('NotEnoughFriends', 3), - - /// Friends list must be less than max friends - maxFriends('MaxFriends', 4), - - /// Friends list must be sorted and free of duplicates - notSorted('NotSorted', 5), - - /// This account is not set up for recovery - notRecoverable('NotRecoverable', 6), - - /// This account is already set up for recovery - alreadyRecoverable('AlreadyRecoverable', 7), - - /// A recovery process has already started for this account - alreadyStarted('AlreadyStarted', 8), - - /// A recovery process has not started for this rescuer - notStarted('NotStarted', 9), - - /// This account is not a friend who can vouch - notFriend('NotFriend', 10), - - /// The friend must wait until the delay period to vouch for this recovery - delayPeriod('DelayPeriod', 11), - - /// This user has already vouched for this recovery - alreadyVouched('AlreadyVouched', 12), - - /// The threshold for recovering this account has not been met - threshold('Threshold', 13), - - /// There are still active recovery attempts that need to be closed - stillActive('StillActive', 14), - - /// This account is already set up for recovery - alreadyProxy('AlreadyProxy', 15), - - /// Some internal state is broken. - badState('BadState', 16); - - const Error(this.variantName, this.codecIndex); - - factory Error.decode(_i1.Input input) { - return codec.decode(input); - } - - final String variantName; - - final int codecIndex; - - static const $ErrorCodec codec = $ErrorCodec(); - - String toJson() => variantName; - _i2.Uint8List encode() { - return codec.encode(this); - } -} - -class $ErrorCodec with _i1.Codec { - const $ErrorCodec(); - - @override - Error decode(_i1.Input input) { - final index = _i1.U8Codec.codec.decode(input); - switch (index) { - case 0: - return Error.notAllowed; - case 1: - return Error.callNotAllowedForHighSecurity; - case 2: - return Error.zeroThreshold; - case 3: - return Error.notEnoughFriends; - case 4: - return Error.maxFriends; - case 5: - return Error.notSorted; - case 6: - return Error.notRecoverable; - case 7: - return Error.alreadyRecoverable; - case 8: - return Error.alreadyStarted; - case 9: - return Error.notStarted; - case 10: - return Error.notFriend; - case 11: - return Error.delayPeriod; - case 12: - return Error.alreadyVouched; - case 13: - return Error.threshold; - case 14: - return Error.stillActive; - case 15: - return Error.alreadyProxy; - case 16: - return Error.badState; - default: - throw Exception('Error: Invalid variant index: "$index"'); - } - } - - @override - void encodeTo(Error value, _i1.Output output) { - _i1.U8Codec.codec.encodeTo(value.codecIndex, output); - } -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/event.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/event.dart deleted file mode 100644 index 6544b0b11..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/pallet/event.dart +++ /dev/null @@ -1,477 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i2; - -import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i5; - -import '../../sp_core/crypto/account_id32.dart' as _i3; -import '../deposit_kind.dart' as _i4; - -/// Events type. -abstract class Event { - const Event(); - - factory Event.decode(_i1.Input input) { - return codec.decode(input); - } - - static const $EventCodec codec = $EventCodec(); - - static const $Event values = $Event(); - - _i2.Uint8List encode() { - final output = _i1.ByteOutput(codec.sizeHint(this)); - codec.encodeTo(this, output); - return output.toBytes(); - } - - int sizeHint() { - return codec.sizeHint(this); - } - - Map> toJson(); -} - -class $Event { - const $Event(); - - RecoveryCreated recoveryCreated({required _i3.AccountId32 account}) { - return RecoveryCreated(account: account); - } - - RecoveryInitiated recoveryInitiated({required _i3.AccountId32 lostAccount, required _i3.AccountId32 rescuerAccount}) { - return RecoveryInitiated(lostAccount: lostAccount, rescuerAccount: rescuerAccount); - } - - RecoveryVouched recoveryVouched({ - required _i3.AccountId32 lostAccount, - required _i3.AccountId32 rescuerAccount, - required _i3.AccountId32 sender, - }) { - return RecoveryVouched(lostAccount: lostAccount, rescuerAccount: rescuerAccount, sender: sender); - } - - RecoveryClosed recoveryClosed({required _i3.AccountId32 lostAccount, required _i3.AccountId32 rescuerAccount}) { - return RecoveryClosed(lostAccount: lostAccount, rescuerAccount: rescuerAccount); - } - - AccountRecovered accountRecovered({required _i3.AccountId32 lostAccount, required _i3.AccountId32 rescuerAccount}) { - return AccountRecovered(lostAccount: lostAccount, rescuerAccount: rescuerAccount); - } - - RecoveryRemoved recoveryRemoved({required _i3.AccountId32 lostAccount}) { - return RecoveryRemoved(lostAccount: lostAccount); - } - - DepositPoked depositPoked({ - required _i3.AccountId32 who, - required _i4.DepositKind kind, - required BigInt oldDeposit, - required BigInt newDeposit, - }) { - return DepositPoked(who: who, kind: kind, oldDeposit: oldDeposit, newDeposit: newDeposit); - } -} - -class $EventCodec with _i1.Codec { - const $EventCodec(); - - @override - Event decode(_i1.Input input) { - final index = _i1.U8Codec.codec.decode(input); - switch (index) { - case 0: - return RecoveryCreated._decode(input); - case 1: - return RecoveryInitiated._decode(input); - case 2: - return RecoveryVouched._decode(input); - case 3: - return RecoveryClosed._decode(input); - case 4: - return AccountRecovered._decode(input); - case 5: - return RecoveryRemoved._decode(input); - case 6: - return DepositPoked._decode(input); - default: - throw Exception('Event: Invalid variant index: "$index"'); - } - } - - @override - void encodeTo(Event value, _i1.Output output) { - switch (value.runtimeType) { - case RecoveryCreated: - (value as RecoveryCreated).encodeTo(output); - break; - case RecoveryInitiated: - (value as RecoveryInitiated).encodeTo(output); - break; - case RecoveryVouched: - (value as RecoveryVouched).encodeTo(output); - break; - case RecoveryClosed: - (value as RecoveryClosed).encodeTo(output); - break; - case AccountRecovered: - (value as AccountRecovered).encodeTo(output); - break; - case RecoveryRemoved: - (value as RecoveryRemoved).encodeTo(output); - break; - case DepositPoked: - (value as DepositPoked).encodeTo(output); - break; - default: - throw Exception('Event: Unsupported "$value" of type "${value.runtimeType}"'); - } - } - - @override - int sizeHint(Event value) { - switch (value.runtimeType) { - case RecoveryCreated: - return (value as RecoveryCreated)._sizeHint(); - case RecoveryInitiated: - return (value as RecoveryInitiated)._sizeHint(); - case RecoveryVouched: - return (value as RecoveryVouched)._sizeHint(); - case RecoveryClosed: - return (value as RecoveryClosed)._sizeHint(); - case AccountRecovered: - return (value as AccountRecovered)._sizeHint(); - case RecoveryRemoved: - return (value as RecoveryRemoved)._sizeHint(); - case DepositPoked: - return (value as DepositPoked)._sizeHint(); - default: - throw Exception('Event: Unsupported "$value" of type "${value.runtimeType}"'); - } - } -} - -/// A recovery process has been set up for an account. -class RecoveryCreated extends Event { - const RecoveryCreated({required this.account}); - - factory RecoveryCreated._decode(_i1.Input input) { - return RecoveryCreated(account: const _i1.U8ArrayCodec(32).decode(input)); - } - - /// T::AccountId - final _i3.AccountId32 account; - - @override - Map>> toJson() => { - 'RecoveryCreated': {'account': account.toList()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(account); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(0, output); - const _i1.U8ArrayCodec(32).encodeTo(account, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is RecoveryCreated && _i5.listsEqual(other.account, account); - - @override - int get hashCode => account.hashCode; -} - -/// A recovery process has been initiated for lost account by rescuer account. -class RecoveryInitiated extends Event { - const RecoveryInitiated({required this.lostAccount, required this.rescuerAccount}); - - factory RecoveryInitiated._decode(_i1.Input input) { - return RecoveryInitiated( - lostAccount: const _i1.U8ArrayCodec(32).decode(input), - rescuerAccount: const _i1.U8ArrayCodec(32).decode(input), - ); - } - - /// T::AccountId - final _i3.AccountId32 lostAccount; - - /// T::AccountId - final _i3.AccountId32 rescuerAccount; - - @override - Map>> toJson() => { - 'RecoveryInitiated': {'lostAccount': lostAccount.toList(), 'rescuerAccount': rescuerAccount.toList()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(lostAccount); - size = size + const _i3.AccountId32Codec().sizeHint(rescuerAccount); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(1, output); - const _i1.U8ArrayCodec(32).encodeTo(lostAccount, output); - const _i1.U8ArrayCodec(32).encodeTo(rescuerAccount, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecoveryInitiated && - _i5.listsEqual(other.lostAccount, lostAccount) && - _i5.listsEqual(other.rescuerAccount, rescuerAccount); - - @override - int get hashCode => Object.hash(lostAccount, rescuerAccount); -} - -/// A recovery process for lost account by rescuer account has been vouched for by sender. -class RecoveryVouched extends Event { - const RecoveryVouched({required this.lostAccount, required this.rescuerAccount, required this.sender}); - - factory RecoveryVouched._decode(_i1.Input input) { - return RecoveryVouched( - lostAccount: const _i1.U8ArrayCodec(32).decode(input), - rescuerAccount: const _i1.U8ArrayCodec(32).decode(input), - sender: const _i1.U8ArrayCodec(32).decode(input), - ); - } - - /// T::AccountId - final _i3.AccountId32 lostAccount; - - /// T::AccountId - final _i3.AccountId32 rescuerAccount; - - /// T::AccountId - final _i3.AccountId32 sender; - - @override - Map>> toJson() => { - 'RecoveryVouched': { - 'lostAccount': lostAccount.toList(), - 'rescuerAccount': rescuerAccount.toList(), - 'sender': sender.toList(), - }, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(lostAccount); - size = size + const _i3.AccountId32Codec().sizeHint(rescuerAccount); - size = size + const _i3.AccountId32Codec().sizeHint(sender); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(2, output); - const _i1.U8ArrayCodec(32).encodeTo(lostAccount, output); - const _i1.U8ArrayCodec(32).encodeTo(rescuerAccount, output); - const _i1.U8ArrayCodec(32).encodeTo(sender, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecoveryVouched && - _i5.listsEqual(other.lostAccount, lostAccount) && - _i5.listsEqual(other.rescuerAccount, rescuerAccount) && - _i5.listsEqual(other.sender, sender); - - @override - int get hashCode => Object.hash(lostAccount, rescuerAccount, sender); -} - -/// A recovery process for lost account by rescuer account has been closed. -class RecoveryClosed extends Event { - const RecoveryClosed({required this.lostAccount, required this.rescuerAccount}); - - factory RecoveryClosed._decode(_i1.Input input) { - return RecoveryClosed( - lostAccount: const _i1.U8ArrayCodec(32).decode(input), - rescuerAccount: const _i1.U8ArrayCodec(32).decode(input), - ); - } - - /// T::AccountId - final _i3.AccountId32 lostAccount; - - /// T::AccountId - final _i3.AccountId32 rescuerAccount; - - @override - Map>> toJson() => { - 'RecoveryClosed': {'lostAccount': lostAccount.toList(), 'rescuerAccount': rescuerAccount.toList()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(lostAccount); - size = size + const _i3.AccountId32Codec().sizeHint(rescuerAccount); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(3, output); - const _i1.U8ArrayCodec(32).encodeTo(lostAccount, output); - const _i1.U8ArrayCodec(32).encodeTo(rescuerAccount, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecoveryClosed && - _i5.listsEqual(other.lostAccount, lostAccount) && - _i5.listsEqual(other.rescuerAccount, rescuerAccount); - - @override - int get hashCode => Object.hash(lostAccount, rescuerAccount); -} - -/// Lost account has been successfully recovered by rescuer account. -class AccountRecovered extends Event { - const AccountRecovered({required this.lostAccount, required this.rescuerAccount}); - - factory AccountRecovered._decode(_i1.Input input) { - return AccountRecovered( - lostAccount: const _i1.U8ArrayCodec(32).decode(input), - rescuerAccount: const _i1.U8ArrayCodec(32).decode(input), - ); - } - - /// T::AccountId - final _i3.AccountId32 lostAccount; - - /// T::AccountId - final _i3.AccountId32 rescuerAccount; - - @override - Map>> toJson() => { - 'AccountRecovered': {'lostAccount': lostAccount.toList(), 'rescuerAccount': rescuerAccount.toList()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(lostAccount); - size = size + const _i3.AccountId32Codec().sizeHint(rescuerAccount); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(4, output); - const _i1.U8ArrayCodec(32).encodeTo(lostAccount, output); - const _i1.U8ArrayCodec(32).encodeTo(rescuerAccount, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is AccountRecovered && - _i5.listsEqual(other.lostAccount, lostAccount) && - _i5.listsEqual(other.rescuerAccount, rescuerAccount); - - @override - int get hashCode => Object.hash(lostAccount, rescuerAccount); -} - -/// A recovery process has been removed for an account. -class RecoveryRemoved extends Event { - const RecoveryRemoved({required this.lostAccount}); - - factory RecoveryRemoved._decode(_i1.Input input) { - return RecoveryRemoved(lostAccount: const _i1.U8ArrayCodec(32).decode(input)); - } - - /// T::AccountId - final _i3.AccountId32 lostAccount; - - @override - Map>> toJson() => { - 'RecoveryRemoved': {'lostAccount': lostAccount.toList()}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(lostAccount); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(5, output); - const _i1.U8ArrayCodec(32).encodeTo(lostAccount, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || other is RecoveryRemoved && _i5.listsEqual(other.lostAccount, lostAccount); - - @override - int get hashCode => lostAccount.hashCode; -} - -/// A deposit has been updated. -class DepositPoked extends Event { - const DepositPoked({required this.who, required this.kind, required this.oldDeposit, required this.newDeposit}); - - factory DepositPoked._decode(_i1.Input input) { - return DepositPoked( - who: const _i1.U8ArrayCodec(32).decode(input), - kind: _i4.DepositKind.codec.decode(input), - oldDeposit: _i1.U128Codec.codec.decode(input), - newDeposit: _i1.U128Codec.codec.decode(input), - ); - } - - /// T::AccountId - final _i3.AccountId32 who; - - /// DepositKind - final _i4.DepositKind kind; - - /// BalanceOf - final BigInt oldDeposit; - - /// BalanceOf - final BigInt newDeposit; - - @override - Map> toJson() => { - 'DepositPoked': {'who': who.toList(), 'kind': kind.toJson(), 'oldDeposit': oldDeposit, 'newDeposit': newDeposit}, - }; - - int _sizeHint() { - int size = 1; - size = size + const _i3.AccountId32Codec().sizeHint(who); - size = size + _i4.DepositKind.codec.sizeHint(kind); - size = size + _i1.U128Codec.codec.sizeHint(oldDeposit); - size = size + _i1.U128Codec.codec.sizeHint(newDeposit); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(6, output); - const _i1.U8ArrayCodec(32).encodeTo(who, output); - _i4.DepositKind.codec.encodeTo(kind, output); - _i1.U128Codec.codec.encodeTo(oldDeposit, output); - _i1.U128Codec.codec.encodeTo(newDeposit, output); - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is DepositPoked && - _i5.listsEqual(other.who, who) && - other.kind == kind && - other.oldDeposit == oldDeposit && - other.newDeposit == newDeposit; - - @override - int get hashCode => Object.hash(who, kind, oldDeposit, newDeposit); -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_recovery/recovery_config.dart b/quantus_sdk/lib/generated/planck/types/pallet_recovery/recovery_config.dart deleted file mode 100644 index 2a6992a2f..000000000 --- a/quantus_sdk/lib/generated/planck/types/pallet_recovery/recovery_config.dart +++ /dev/null @@ -1,89 +0,0 @@ -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:typed_data' as _i3; - -import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i4; - -import '../sp_core/crypto/account_id32.dart' as _i2; - -class RecoveryConfig { - const RecoveryConfig({ - required this.delayPeriod, - required this.deposit, - required this.friends, - required this.threshold, - }); - - factory RecoveryConfig.decode(_i1.Input input) { - return codec.decode(input); - } - - /// BlockNumber - final int delayPeriod; - - /// Balance - final BigInt deposit; - - /// Friends - final List<_i2.AccountId32> friends; - - /// u16 - final int threshold; - - static const $RecoveryConfigCodec codec = $RecoveryConfigCodec(); - - _i3.Uint8List encode() { - return codec.encode(this); - } - - Map toJson() => { - 'delayPeriod': delayPeriod, - 'deposit': deposit, - 'friends': friends.map((value) => value.toList()).toList(), - 'threshold': threshold, - }; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecoveryConfig && - other.delayPeriod == delayPeriod && - other.deposit == deposit && - _i4.listsEqual(other.friends, friends) && - other.threshold == threshold; - - @override - int get hashCode => Object.hash(delayPeriod, deposit, friends, threshold); -} - -class $RecoveryConfigCodec with _i1.Codec { - const $RecoveryConfigCodec(); - - @override - void encodeTo(RecoveryConfig obj, _i1.Output output) { - _i1.U32Codec.codec.encodeTo(obj.delayPeriod, output); - _i1.U128Codec.codec.encodeTo(obj.deposit, output); - const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).encodeTo(obj.friends, output); - _i1.U16Codec.codec.encodeTo(obj.threshold, output); - } - - @override - RecoveryConfig decode(_i1.Input input) { - return RecoveryConfig( - delayPeriod: _i1.U32Codec.codec.decode(input), - deposit: _i1.U128Codec.codec.decode(input), - friends: const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).decode(input), - threshold: _i1.U16Codec.codec.decode(input), - ); - } - - @override - int sizeHint(RecoveryConfig obj) { - int size = 0; - size = size + _i1.U32Codec.codec.sizeHint(obj.delayPeriod); - size = size + _i1.U128Codec.codec.sizeHint(obj.deposit); - size = size + const _i1.SequenceCodec<_i2.AccountId32>(_i2.AccountId32Codec()).sizeHint(obj.friends); - size = size + _i1.U16Codec.codec.sizeHint(obj.threshold); - return size; - } -} diff --git a/quantus_sdk/lib/generated/planck/types/pallet_referenda/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_referenda/pallet/error.dart index 8b601d376..530e3db71 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_referenda/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_referenda/pallet/error.dart @@ -72,6 +72,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/call.dart b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/call.dart index 2434fc190..08022436b 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/call.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/call.dart @@ -277,6 +277,15 @@ class Cancel extends Call { /// /// - `tx_id`: The unique identifier of the pending transfer to execute. /// +/// Execution uses `transfer_allow_death` so a sender who spent their leftover +/// free balance during the delay still completes. A failed inner transfer (e.g. +/// dest overflow, or `amount < ED` to a new account) does not fail this +/// extrinsic: the hold is already released and the pending transfer is already +/// removed. Propagating that error would roll back those writes (FRAME +/// dispatchables are transactional) while Scheduler terminally drops the named +/// task, freezing the funds with no retry. The inner result is still recorded on +/// [`Event::TransactionExecuted`]. +/// /// # Errors /// /// - [`InvalidSchedulerOrigin`](Error::InvalidSchedulerOrigin): Called by an account other diff --git a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/error.dart index 2d605cda6..b41af90b7 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/error.dart @@ -65,6 +65,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/hold_reason.dart b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/hold_reason.dart index bca8ed2f5..12bf569ff 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/hold_reason.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_reversible_transfers/pallet/hold_reason.dart @@ -19,6 +19,7 @@ enum HoldReason { static const $HoldReasonCodec codec = $HoldReasonCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_scheduler/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_scheduler/pallet/error.dart index bf572bb76..35376eba2 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_scheduler/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_scheduler/pallet/error.dart @@ -54,6 +54,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_transaction_payment/releases.dart b/quantus_sdk/lib/generated/planck/types/pallet_transaction_payment/releases.dart index a01f00eed..8669e3ca9 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_transaction_payment/releases.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_transaction_payment/releases.dart @@ -20,6 +20,7 @@ enum Releases { static const $ReleasesCodec codec = $ReleasesCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_treasury/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_treasury/pallet/error.dart index 259396f83..d684be739 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_treasury/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_treasury/pallet/error.dart @@ -21,6 +21,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/error.dart index 46a716b77..7ad7ae191 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/error.dart @@ -24,6 +24,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/event.dart b/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/event.dart index 6d1d280c2..425a06068 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/event.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_utility/pallet/event.dart @@ -24,6 +24,7 @@ enum Event { static const $EventCodec codec = $EventCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/call.dart b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/call.dart index 3329f0b20..bd9962ea3 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/call.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/call.dart @@ -117,7 +117,9 @@ class $CallCodec with _i1.Codec { /// Pay the largest valid claim on `schedule_id` to its beneficiary. Payouts are /// rounded down to [`Config::PayoutQuantum`], must meet [`Config::MinimumPayout`], /// and reserve at least one minimum-sized final claim unless the schedule is fully -/// vested. +/// vested. Non-final payouts are further rounded down to +/// [`NON_FINAL_PAYOUT_QUANTA`] leaf quanta; the leftover stays on the schedule +/// until a later claim or the exact final payout. /// /// Permissionless: any signed account may call this for any schedule; the payout /// always goes to the stored beneficiary. This is the only claim path for @@ -235,13 +237,11 @@ class CreateSchedule extends Call { int get hashCode => Object.hash(beneficiary, start, cliff, end, total); } -/// End a schedule early: the still-unpaid vested part (rounded down to a -/// [`Config::PayoutQuantum`] multiple) goes to the beneficiary, everything else -/// this schedule still holds — the unvested remainder plus any sub-quantum -/// vested dust — returns to the treasury, and the schedule is removed. The -/// treasury is signature-controlled and needs no wormhole leaf, so dust is safe -/// there but would be stranded on a keyless beneficiary. A non-zero beneficiary -/// payout below [`Config::MinimumPayout`] is rejected without ending the schedule. +/// End a schedule early: the still-unpaid vested part (rounded to the nearest +/// [`Config::PayoutQuantum`]) goes to the beneficiary if it meets +/// [`Config::MinimumPayout`]; otherwise that sliver is refunded with the +/// unvested remainder. The treasury is signature-controlled and needs no +/// wormhole leaf, so the refund is not quantized and never blocks ending. class EndSchedule extends Call { const EndSchedule({required this.scheduleId}); @@ -275,8 +275,12 @@ class EndSchedule extends Call { int get hashCode => scheduleId.hashCode; } -/// Settle any payout a permissionless claim could currently force, then change the -/// beneficiary. This makes retargeting independent of claim transaction ordering. +/// Change the schedule's beneficiary without paying anything out. A retarget +/// replaces the wallet of the *same* grantee (lost-key remedy): the old address +/// may be lost or stolen, so settling it would burn funds or pay the thief. +/// Everything vested but unclaimed stays on the schedule and goes to the new +/// wallet at its next claim. (A permissionless claim landing before the +/// retarget still pays the old address, so rotate promptly.) class RetargetSchedule extends Call { const RetargetSchedule({required this.scheduleId, required this.newBeneficiary}); diff --git a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/error.dart index 837fddfd0..d5b00952a 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/error.dart @@ -24,22 +24,19 @@ enum Error { /// entire remainder has vested. claimWouldLeaveDust('ClaimWouldLeaveDust', 4), - /// Ending now would emit a non-zero beneficiary payout below the minimum. - payoutBelowMinimum('PayoutBelowMinimum', 5), - /// The treasury account is not configured or aliases the vesting pot. - treasuryNotConfigured('TreasuryNotConfigured', 6), + treasuryNotConfigured('TreasuryNotConfigured', 5), /// The pot does not hold its existential-deposit buffer; endow it first. - potUnderfunded('PotUnderfunded', 7), + potUnderfunded('PotUnderfunded', 6), /// The beneficiary must not be the pot, and retargeting must change the account. - invalidBeneficiary('InvalidBeneficiary', 8), + invalidBeneficiary('InvalidBeneficiary', 7), /// The proof recorder reported the payout credit as dropped: no wormhole leaf /// was created, so the payout is rolled back rather than finalized without the /// proof material a keyless beneficiary needs to exit. - payoutProofNotRecorded('PayoutProofNotRecorded', 9); + payoutProofNotRecorded('PayoutProofNotRecorded', 8); const Error(this.variantName, this.codecIndex); @@ -54,6 +51,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } @@ -77,14 +75,12 @@ class $ErrorCodec with _i1.Codec { case 4: return Error.claimWouldLeaveDust; case 5: - return Error.payoutBelowMinimum; - case 6: return Error.treasuryNotConfigured; - case 7: + case 6: return Error.potUnderfunded; - case 8: + case 7: return Error.invalidBeneficiary; - case 9: + case 8: return Error.payoutProofNotRecorded; default: throw Exception('Error: Invalid variant index: "$index"'); diff --git a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/event.dart b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/event.dart index 54f1ab094..55ecc66f9 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/event.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_vesting/pallet/event.dart @@ -74,14 +74,8 @@ class $Event { required BigInt scheduleId, required _i3.AccountId32 oldBeneficiary, required _i3.AccountId32 newBeneficiary, - required BigInt vestedPaid, }) { - return ScheduleRetargeted( - scheduleId: scheduleId, - oldBeneficiary: oldBeneficiary, - newBeneficiary: newBeneficiary, - vestedPaid: vestedPaid, - ); + return ScheduleRetargeted(scheduleId: scheduleId, oldBeneficiary: oldBeneficiary, newBeneficiary: newBeneficiary); } } @@ -354,21 +348,17 @@ class ScheduleEnded extends Event { int get hashCode => Object.hash(scheduleId, beneficiary, vestedPaid, unvestedReturned); } -/// A schedule's beneficiary was changed after settling any currently claimable payout. +/// A schedule's beneficiary was changed. Nothing was paid out: the retarget +/// replaces the same grantee's wallet, so the accrued entitlement follows the +/// schedule to the new address. class ScheduleRetargeted extends Event { - const ScheduleRetargeted({ - required this.scheduleId, - required this.oldBeneficiary, - required this.newBeneficiary, - required this.vestedPaid, - }); + const ScheduleRetargeted({required this.scheduleId, required this.oldBeneficiary, required this.newBeneficiary}); factory ScheduleRetargeted._decode(_i1.Input input) { return ScheduleRetargeted( scheduleId: _i1.U64Codec.codec.decode(input), oldBeneficiary: const _i1.U8ArrayCodec(32).decode(input), newBeneficiary: const _i1.U8ArrayCodec(32).decode(input), - vestedPaid: _i1.U128Codec.codec.decode(input), ); } @@ -381,16 +371,12 @@ class ScheduleRetargeted extends Event { /// T::AccountId final _i3.AccountId32 newBeneficiary; - /// BalanceOf - final BigInt vestedPaid; - @override Map> toJson() => { 'ScheduleRetargeted': { 'scheduleId': scheduleId, 'oldBeneficiary': oldBeneficiary.toList(), 'newBeneficiary': newBeneficiary.toList(), - 'vestedPaid': vestedPaid, }, }; @@ -399,7 +385,6 @@ class ScheduleRetargeted extends Event { size = size + _i1.U64Codec.codec.sizeHint(scheduleId); size = size + const _i3.AccountId32Codec().sizeHint(oldBeneficiary); size = size + const _i3.AccountId32Codec().sizeHint(newBeneficiary); - size = size + _i1.U128Codec.codec.sizeHint(vestedPaid); return size; } @@ -408,7 +393,6 @@ class ScheduleRetargeted extends Event { _i1.U64Codec.codec.encodeTo(scheduleId, output); const _i1.U8ArrayCodec(32).encodeTo(oldBeneficiary, output); const _i1.U8ArrayCodec(32).encodeTo(newBeneficiary, output); - _i1.U128Codec.codec.encodeTo(vestedPaid, output); } @override @@ -417,9 +401,8 @@ class ScheduleRetargeted extends Event { other is ScheduleRetargeted && other.scheduleId == scheduleId && _i4.listsEqual(other.oldBeneficiary, oldBeneficiary) && - _i4.listsEqual(other.newBeneficiary, newBeneficiary) && - other.vestedPaid == vestedPaid; + _i4.listsEqual(other.newBeneficiary, newBeneficiary); @override - int get hashCode => Object.hash(scheduleId, oldBeneficiary, newBeneficiary, vestedPaid); + int get hashCode => Object.hash(scheduleId, oldBeneficiary, newBeneficiary); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_wormhole/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_wormhole/pallet/error.dart index b3ff89196..5bd63115e 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_wormhole/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_wormhole/pallet/error.dart @@ -12,9 +12,9 @@ enum Error { /// proof does). nullifierAlreadyUsed('NullifierAlreadyUsed', 1), - /// The bundle contains only dummy (all-zero) padding segments, so there is - /// nothing to exit. Distinct from [`Error::NullifierAlreadyUsed`], which is a - /// replay of real segments. + /// The bundle has nothing to settle: only dummy (all-zero) padding, or + /// every valid segment exits zero. Distinct from [`Error::NullifierAlreadyUsed`], + /// which is a replay of real segments. noValidSegments('NoValidSegments', 2), blockNotFound('BlockNotFound', 3), verifierNotAvailable('VerifierNotAvailable', 4), @@ -50,6 +50,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/error.dart b/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/error.dart index f355decfc..638934490 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/error.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/error.dart @@ -9,7 +9,11 @@ enum Error { leafIndexOutOfBounds('LeafIndexOutOfBounds', 0), /// Leaf not found. - leafNotFound('LeafNotFound', 1); + leafNotFound('LeafNotFound', 1), + + /// Leaf was appended this block and is not yet folded into the root; it + /// becomes provable once the block is finalized. + leafNotYetSettled('LeafNotYetSettled', 2); const Error(this.variantName, this.codecIndex); @@ -24,6 +28,7 @@ enum Error { static const $ErrorCodec codec = $ErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } @@ -40,6 +45,8 @@ class $ErrorCodec with _i1.Codec { return Error.leafIndexOutOfBounds; case 1: return Error.leafNotFound; + case 2: + return Error.leafNotYetSettled; default: throw Exception('Error: Invalid variant index: "$index"'); } diff --git a/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/event.dart b/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/event.dart index 0f235cacd..ba34a08ba 100644 --- a/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/event.dart +++ b/quantus_sdk/lib/generated/planck/types/pallet_zk_tree/pallet/event.dart @@ -2,7 +2,6 @@ import 'dart:typed_data' as _i2; import 'package:polkadart/scale_codec.dart' as _i1; -import 'package:quiver/collection.dart' as _i3; /// The `Event` enum of this pallet abstract class Event { @@ -32,8 +31,8 @@ abstract class Event { class $Event { const $Event(); - LeafInserted leafInserted({required BigInt index, required List leafHash, required List newRoot}) { - return LeafInserted(index: index, leafHash: leafHash, newRoot: newRoot); + LeafInserted leafInserted({required BigInt index}) { + return LeafInserted(index: index); } TreeGrew treeGrew({required int newDepth}) { @@ -84,57 +83,42 @@ class $EventCodec with _i1.Codec { } } -/// A new leaf was inserted into the tree. +/// A new leaf was inserted into the tree. The root including this leaf is +/// computed at the end of the block and published in the block header. The +/// leaf hash is deliberately not included: it is derivable from `Leaves` +/// (and served by the RPC), and hashing it here would double the per-leaf +/// Poseidon work the batched settlement saves. class LeafInserted extends Event { - const LeafInserted({required this.index, required this.leafHash, required this.newRoot}); + const LeafInserted({required this.index}); factory LeafInserted._decode(_i1.Input input) { - return LeafInserted( - index: _i1.U64Codec.codec.decode(input), - leafHash: const _i1.U8ArrayCodec(32).decode(input), - newRoot: const _i1.U8ArrayCodec(32).decode(input), - ); + return LeafInserted(index: _i1.U64Codec.codec.decode(input)); } /// u64 final BigInt index; - /// Hash256 - final List leafHash; - - /// Hash256 - final List newRoot; - @override - Map> toJson() => { - 'LeafInserted': {'index': index, 'leafHash': leafHash.toList(), 'newRoot': newRoot.toList()}, + Map> toJson() => { + 'LeafInserted': {'index': index}, }; int _sizeHint() { int size = 1; size = size + _i1.U64Codec.codec.sizeHint(index); - size = size + const _i1.U8ArrayCodec(32).sizeHint(leafHash); - size = size + const _i1.U8ArrayCodec(32).sizeHint(newRoot); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(0, output); _i1.U64Codec.codec.encodeTo(index, output); - const _i1.U8ArrayCodec(32).encodeTo(leafHash, output); - const _i1.U8ArrayCodec(32).encodeTo(newRoot, output); } @override - bool operator ==(Object other) => - identical(this, other) || - other is LeafInserted && - other.index == index && - _i3.listsEqual(other.leafHash, leafHash) && - _i3.listsEqual(other.newRoot, newRoot); + bool operator ==(Object other) => identical(this, other) || other is LeafInserted && other.index == index; @override - int get hashCode => Object.hash(index, leafHash, newRoot); + int get hashCode => index.hashCode; } /// Tree depth increased. diff --git a/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_call.dart b/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_call.dart index baf8d4648..3bf0f6390 100644 --- a/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_call.dart +++ b/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_call.dart @@ -5,17 +5,16 @@ import 'package:polkadart/scale_codec.dart' as _i1; import '../frame_system/pallet/call.dart' as _i3; import '../pallet_balances/pallet/call.dart' as _i5; -import '../pallet_multisig/pallet/call.dart' as _i13; +import '../pallet_multisig/pallet/call.dart' as _i12; import '../pallet_preimage/pallet/call.dart' as _i6; import '../pallet_ranked_collective/pallet/call.dart' as _i9; -import '../pallet_recovery/pallet/call.dart' as _i12; import '../pallet_referenda/pallet/call.dart' as _i10; import '../pallet_reversible_transfers/pallet/call.dart' as _i8; import '../pallet_timestamp/pallet/call.dart' as _i4; import '../pallet_treasury/pallet/call.dart' as _i11; import '../pallet_utility/pallet/call.dart' as _i7; -import '../pallet_vesting/pallet/call.dart' as _i15; -import '../pallet_wormhole/pallet/call.dart' as _i14; +import '../pallet_vesting/pallet/call.dart' as _i14; +import '../pallet_wormhole/pallet/call.dart' as _i13; abstract class RuntimeCall { const RuntimeCall(); @@ -38,7 +37,7 @@ abstract class RuntimeCall { return codec.sizeHint(this); } - Map> toJson(); + Map>> toJson(); } class $RuntimeCall { @@ -80,19 +79,15 @@ class $RuntimeCall { return TreasuryPallet(value0); } - Recovery recovery(_i12.Call value0) { - return Recovery(value0); - } - - Multisig multisig(_i13.Call value0) { + Multisig multisig(_i12.Call value0) { return Multisig(value0); } - Wormhole wormhole(_i14.Call value0) { + Wormhole wormhole(_i13.Call value0) { return Wormhole(value0); } - Vesting vesting(_i15.Call value0) { + Vesting vesting(_i14.Call value0) { return Vesting(value0); } } @@ -122,8 +117,6 @@ class $RuntimeCallCodec with _i1.Codec { return TechReferenda._decode(input); case 15: return TreasuryPallet._decode(input); - case 16: - return Recovery._decode(input); case 19: return Multisig._decode(input); case 20: @@ -165,9 +158,6 @@ class $RuntimeCallCodec with _i1.Codec { case TreasuryPallet: (value as TreasuryPallet).encodeTo(output); break; - case Recovery: - (value as Recovery).encodeTo(output); - break; case Multisig: (value as Multisig).encodeTo(output); break; @@ -203,8 +193,6 @@ class $RuntimeCallCodec with _i1.Codec { return (value as TechReferenda)._sizeHint(); case TreasuryPallet: return (value as TreasuryPallet)._sizeHint(); - case Recovery: - return (value as Recovery)._sizeHint(); case Multisig: return (value as Multisig)._sizeHint(); case Wormhole: @@ -505,61 +493,29 @@ class TreasuryPallet extends RuntimeCall { int get hashCode => value0.hashCode; } -class Recovery extends RuntimeCall { - const Recovery(this.value0); - - factory Recovery._decode(_i1.Input input) { - return Recovery(_i12.Call.codec.decode(input)); - } - - /// self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch - ///::CallableCallFor - final _i12.Call value0; - - @override - Map> toJson() => {'Recovery': value0.toJson()}; - - int _sizeHint() { - int size = 1; - size = size + _i12.Call.codec.sizeHint(value0); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(16, output); - _i12.Call.codec.encodeTo(value0, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is Recovery && other.value0 == value0; - - @override - int get hashCode => value0.hashCode; -} - class Multisig extends RuntimeCall { const Multisig(this.value0); factory Multisig._decode(_i1.Input input) { - return Multisig(_i13.Call.codec.decode(input)); + return Multisig(_i12.Call.codec.decode(input)); } /// self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch ///::CallableCallFor - final _i13.Call value0; + final _i12.Call value0; @override Map>> toJson() => {'Multisig': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i13.Call.codec.sizeHint(value0); + size = size + _i12.Call.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(19, output); - _i13.Call.codec.encodeTo(value0, output); + _i12.Call.codec.encodeTo(value0, output); } @override @@ -573,25 +529,25 @@ class Wormhole extends RuntimeCall { const Wormhole(this.value0); factory Wormhole._decode(_i1.Input input) { - return Wormhole(_i14.Call.codec.decode(input)); + return Wormhole(_i13.Call.codec.decode(input)); } /// self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch ///::CallableCallFor - final _i14.Call value0; + final _i13.Call value0; @override Map>>> toJson() => {'Wormhole': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i14.Call.codec.sizeHint(value0); + size = size + _i13.Call.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(20, output); - _i14.Call.codec.encodeTo(value0, output); + _i13.Call.codec.encodeTo(value0, output); } @override @@ -605,25 +561,25 @@ class Vesting extends RuntimeCall { const Vesting(this.value0); factory Vesting._decode(_i1.Input input) { - return Vesting(_i15.Call.codec.decode(input)); + return Vesting(_i14.Call.codec.decode(input)); } /// self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch ///::CallableCallFor - final _i15.Call value0; + final _i14.Call value0; @override Map>> toJson() => {'Vesting': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i15.Call.codec.sizeHint(value0); + size = size + _i14.Call.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(22, output); - _i15.Call.codec.encodeTo(value0, output); + _i14.Call.codec.encodeTo(value0, output); } @override diff --git a/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_event.dart b/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_event.dart index 3fa08785a..3f16c89bb 100644 --- a/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_event.dart +++ b/quantus_sdk/lib/generated/planck/types/quantus_runtime/runtime_event.dart @@ -6,20 +6,19 @@ import 'package:polkadart/scale_codec.dart' as _i1; import '../frame_system/pallet/event.dart' as _i3; import '../pallet_balances/pallet/event.dart' as _i4; import '../pallet_mining_rewards/pallet/event.dart' as _i7; -import '../pallet_multisig/pallet/event.dart' as _i16; +import '../pallet_multisig/pallet/event.dart' as _i15; import '../pallet_preimage/pallet/event.dart' as _i8; import '../pallet_qpow/pallet/event.dart' as _i6; import '../pallet_ranked_collective/pallet/event.dart' as _i12; -import '../pallet_recovery/pallet/event.dart' as _i15; import '../pallet_referenda/pallet/event.dart' as _i13; import '../pallet_reversible_transfers/pallet/event.dart' as _i11; import '../pallet_scheduler/pallet/event.dart' as _i9; import '../pallet_transaction_payment/pallet/event.dart' as _i5; import '../pallet_treasury/pallet/event.dart' as _i14; import '../pallet_utility/pallet/event.dart' as _i10; -import '../pallet_vesting/pallet/event.dart' as _i19; -import '../pallet_wormhole/pallet/event.dart' as _i17; -import '../pallet_zk_tree/pallet/event.dart' as _i18; +import '../pallet_vesting/pallet/event.dart' as _i18; +import '../pallet_wormhole/pallet/event.dart' as _i16; +import '../pallet_zk_tree/pallet/event.dart' as _i17; abstract class RuntimeEvent { const RuntimeEvent(); @@ -96,23 +95,19 @@ class $RuntimeEvent { return TreasuryPallet(value0); } - Recovery recovery(_i15.Event value0) { - return Recovery(value0); - } - - Multisig multisig(_i16.Event value0) { + Multisig multisig(_i15.Event value0) { return Multisig(value0); } - Wormhole wormhole(_i17.Event value0) { + Wormhole wormhole(_i16.Event value0) { return Wormhole(value0); } - ZkTree zkTree(_i18.Event value0) { + ZkTree zkTree(_i17.Event value0) { return ZkTree(value0); } - Vesting vesting(_i19.Event value0) { + Vesting vesting(_i18.Event value0) { return Vesting(value0); } } @@ -148,8 +143,6 @@ class $RuntimeEventCodec with _i1.Codec { return TechReferenda._decode(input); case 15: return TreasuryPallet._decode(input); - case 16: - return Recovery._decode(input); case 19: return Multisig._decode(input); case 20: @@ -202,9 +195,6 @@ class $RuntimeEventCodec with _i1.Codec { case TreasuryPallet: (value as TreasuryPallet).encodeTo(output); break; - case Recovery: - (value as Recovery).encodeTo(output); - break; case Multisig: (value as Multisig).encodeTo(output); break; @@ -249,8 +239,6 @@ class $RuntimeEventCodec with _i1.Codec { return (value as TechReferenda)._sizeHint(); case TreasuryPallet: return (value as TreasuryPallet)._sizeHint(); - case Recovery: - return (value as Recovery)._sizeHint(); case Multisig: return (value as Multisig)._sizeHint(); case Wormhole: @@ -637,59 +625,28 @@ class TreasuryPallet extends RuntimeEvent { int get hashCode => value0.hashCode; } -class Recovery extends RuntimeEvent { - const Recovery(this.value0); - - factory Recovery._decode(_i1.Input input) { - return Recovery(_i15.Event.codec.decode(input)); - } - - /// pallet_recovery::Event - final _i15.Event value0; - - @override - Map>> toJson() => {'Recovery': value0.toJson()}; - - int _sizeHint() { - int size = 1; - size = size + _i15.Event.codec.sizeHint(value0); - return size; - } - - void encodeTo(_i1.Output output) { - _i1.U8Codec.codec.encodeTo(16, output); - _i15.Event.codec.encodeTo(value0, output); - } - - @override - bool operator ==(Object other) => identical(this, other) || other is Recovery && other.value0 == value0; - - @override - int get hashCode => value0.hashCode; -} - class Multisig extends RuntimeEvent { const Multisig(this.value0); factory Multisig._decode(_i1.Input input) { - return Multisig(_i16.Event.codec.decode(input)); + return Multisig(_i15.Event.codec.decode(input)); } /// pallet_multisig::Event - final _i16.Event value0; + final _i15.Event value0; @override Map>> toJson() => {'Multisig': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i16.Event.codec.sizeHint(value0); + size = size + _i15.Event.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(19, output); - _i16.Event.codec.encodeTo(value0, output); + _i15.Event.codec.encodeTo(value0, output); } @override @@ -703,24 +660,24 @@ class Wormhole extends RuntimeEvent { const Wormhole(this.value0); factory Wormhole._decode(_i1.Input input) { - return Wormhole(_i17.Event.codec.decode(input)); + return Wormhole(_i16.Event.codec.decode(input)); } /// pallet_wormhole::Event - final _i17.Event value0; + final _i16.Event value0; @override Map>> toJson() => {'Wormhole': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i17.Event.codec.sizeHint(value0); + size = size + _i16.Event.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(20, output); - _i17.Event.codec.encodeTo(value0, output); + _i16.Event.codec.encodeTo(value0, output); } @override @@ -734,24 +691,24 @@ class ZkTree extends RuntimeEvent { const ZkTree(this.value0); factory ZkTree._decode(_i1.Input input) { - return ZkTree(_i18.Event.codec.decode(input)); + return ZkTree(_i17.Event.codec.decode(input)); } /// pallet_zk_tree::Event - final _i18.Event value0; + final _i17.Event value0; @override Map>> toJson() => {'ZkTree': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i18.Event.codec.sizeHint(value0); + size = size + _i17.Event.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(21, output); - _i18.Event.codec.encodeTo(value0, output); + _i17.Event.codec.encodeTo(value0, output); } @override @@ -765,24 +722,24 @@ class Vesting extends RuntimeEvent { const Vesting(this.value0); factory Vesting._decode(_i1.Input input) { - return Vesting(_i19.Event.codec.decode(input)); + return Vesting(_i18.Event.codec.decode(input)); } /// pallet_vesting::Event - final _i19.Event value0; + final _i18.Event value0; @override Map>> toJson() => {'Vesting': value0.toJson()}; int _sizeHint() { int size = 1; - size = size + _i19.Event.codec.sizeHint(value0); + size = size + _i18.Event.codec.sizeHint(value0); return size; } void encodeTo(_i1.Output output) { _i1.U8Codec.codec.encodeTo(22, output); - _i19.Event.codec.encodeTo(value0, output); + _i18.Event.codec.encodeTo(value0, output); } @override diff --git a/quantus_sdk/lib/generated/planck/types/sp_arithmetic/arithmetic_error.dart b/quantus_sdk/lib/generated/planck/types/sp_arithmetic/arithmetic_error.dart index 743167591..fce65c073 100644 --- a/quantus_sdk/lib/generated/planck/types/sp_arithmetic/arithmetic_error.dart +++ b/quantus_sdk/lib/generated/planck/types/sp_arithmetic/arithmetic_error.dart @@ -21,6 +21,7 @@ enum ArithmeticError { static const $ArithmeticErrorCodec codec = $ArithmeticErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/sp_runtime/proving_trie/trie_error.dart b/quantus_sdk/lib/generated/planck/types/sp_runtime/proving_trie/trie_error.dart index 5beb544bc..f10fe5c00 100644 --- a/quantus_sdk/lib/generated/planck/types/sp_runtime/proving_trie/trie_error.dart +++ b/quantus_sdk/lib/generated/planck/types/sp_runtime/proving_trie/trie_error.dart @@ -32,6 +32,7 @@ enum TrieError { static const $TrieErrorCodec codec = $TrieErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/sp_runtime/token_error.dart b/quantus_sdk/lib/generated/planck/types/sp_runtime/token_error.dart index c16f0c251..38bb9f28e 100644 --- a/quantus_sdk/lib/generated/planck/types/sp_runtime/token_error.dart +++ b/quantus_sdk/lib/generated/planck/types/sp_runtime/token_error.dart @@ -28,6 +28,7 @@ enum TokenError { static const $TokenErrorCodec codec = $TokenErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/generated/planck/types/sp_runtime/transactional_error.dart b/quantus_sdk/lib/generated/planck/types/sp_runtime/transactional_error.dart index 9a3a7f7fa..f556271ee 100644 --- a/quantus_sdk/lib/generated/planck/types/sp_runtime/transactional_error.dart +++ b/quantus_sdk/lib/generated/planck/types/sp_runtime/transactional_error.dart @@ -20,6 +20,7 @@ enum TransactionalError { static const $TransactionalErrorCodec codec = $TransactionalErrorCodec(); String toJson() => variantName; + _i2.Uint8List encode() { return codec.encode(this); } diff --git a/quantus_sdk/lib/quantus_sdk.dart b/quantus_sdk/lib/quantus_sdk.dart index 2a3ec7a2c..ec3699952 100644 --- a/quantus_sdk/lib/quantus_sdk.dart +++ b/quantus_sdk/lib/quantus_sdk.dart @@ -69,7 +69,6 @@ export 'src/services/network/redundant_endpoint.dart'; export 'src/services/locale_number_config.dart'; export 'src/services/number_formatting_service.dart'; export 'src/services/recent_addresses_service.dart'; -export 'src/services/recovery_service.dart'; export 'src/services/reversible_transfers_service.dart'; export 'src/services/settings_service.dart'; export 'src/services/substrate_service.dart'; diff --git a/quantus_sdk/lib/src/chain/call_decoder.dart b/quantus_sdk/lib/src/chain/call_decoder.dart index e05ba6a31..0db2262fb 100644 --- a/quantus_sdk/lib/src/chain/call_decoder.dart +++ b/quantus_sdk/lib/src/chain/call_decoder.dart @@ -29,7 +29,6 @@ import 'package:quantus_sdk/generated/planck/types/pallet_balances/pallet/call.d import 'package:quantus_sdk/generated/planck/types/pallet_multisig/pallet/call.dart' as multisig; import 'package:quantus_sdk/generated/planck/types/pallet_preimage/pallet/call.dart' as preimage; import 'package:quantus_sdk/generated/planck/types/pallet_ranked_collective/pallet/call.dart' as collective; -import 'package:quantus_sdk/generated/planck/types/pallet_recovery/pallet/call.dart' as recovery; import 'package:quantus_sdk/generated/planck/types/pallet_referenda/pallet/call.dart' as referenda; import 'package:quantus_sdk/generated/planck/types/pallet_reversible_transfers/pallet/call.dart' as reversible; import 'package:quantus_sdk/generated/planck/types/pallet_treasury/pallet/call.dart' as treasury; @@ -78,9 +77,23 @@ class CallDecoder { }); } + /// Decodes call bytes into the runtime call they carry, requiring an exact fit. + /// + /// `multisig.execute` carries its inner call inline rather than as the + /// length-prefixed bytes `approve` takes, so a proposal's stored payload has to + /// be decoded before it can be resubmitted. Bytes the bundled metadata cannot + /// read fail here, before signing, instead of building a call the chain rejects. + static runtime.RuntimeCall decodeRuntimeCall( + List bytes, { + required CallPolicy policy, + List within = const [], + }) { + return _asFormatException(() => _decodeExact(bytes, policy, within)); + } + /// The generated codecs signal malformed bytes with their own exception types. /// Callers fail closed on [FormatException], so every rejection arrives as one. - static DecodedCall _asFormatException(DecodedCall Function() decode) { + static T _asFormatException(T Function() decode) { try { return decode(); } on FormatException { @@ -90,7 +103,7 @@ class CallDecoder { } } - static DecodedCall _decodeBytesAtPath(List bytes, CallPolicy policy, List path) { + static runtime.RuntimeCall _decodeExact(List bytes, CallPolicy policy, List path) { _checkCallSize(bytes.length); final input = Input.fromBytes(Uint8List.fromList(bytes)); final call = _decodeCall(input, policy, path); @@ -98,7 +111,11 @@ class CallDecoder { if (remaining != 0) { throw FormatException('$remaining trailing bytes after nested call'); } - return _describe(call, path.length, policy: policy, path: path); + return call; + } + + static DecodedCall _decodeBytesAtPath(List bytes, CallPolicy policy, List path) { + return _describe(_decodeExact(bytes, policy, path), path.length, policy: policy, path: path); } static void _checkCallSize(int length) { @@ -131,7 +148,6 @@ class CallDecoder { runtime.TechReferenda(:final value0) => _referenda(value0, depth, policy, path), runtime.TreasuryPallet(:final value0) => _treasury(value0), runtime.Utility(:final value0) => _utility(value0, depth, policy, path), - runtime.Recovery(:final value0) => _recovery(value0, depth, policy, path), runtime.Vesting(:final value0) => _vesting(value0), runtime.System(:final value0) => _system(value0), _ => _generic(call), @@ -143,10 +159,10 @@ class CallDecoder { // The generated codecs recurse into `RuntimeCall` the moment they meet a // nesting variant, so a limit applied to the decoded tree would already have // paid for the whole recursion — an over-nested payload could exhaust the - // stack instead of being refused. `Utility` and `Recovery` are the only - // pallets that embed a call inline, so decoding just those variants here - // bounds the recursion at its only entry points; every other call is handed - // straight to the generated codec, which cannot recurse. + // stack instead of being refused. `Utility.batch_all` and `Multisig.execute` + // are the only calls that embed a call inline, so decoding just those variants + // here bounds the recursion at its only entry points; every other call is + // handed straight to the generated codec, which cannot recurse. // // Calls carried as length-prefixed bytes (multisig proposals, noted preimages, // inline referendum proposals) do not recurse during decoding at all — they @@ -159,14 +175,6 @@ class CallDecoder { static final Uint8List _batchAll = const runtime.Utility(utility.BatchAll(calls: [])).encode(); static final int _utilityPalletIndex = _batchAll[0]; static final int _batchAllCallIndex = _batchAll[1]; - static final Uint8List _asRecovered = runtime.Recovery( - recovery.AsRecovered( - account: multi_address.Id(Uint8List(32)), - call: const runtime.System(system.Remark(remark: [])), - ), - ).encode(); - static final int _recoveryPalletIndex = _asRecovered[0]; - static final int _asRecoveredCallIndex = _asRecovered[1]; static runtime.RuntimeCall _decodeCall(ByteInput input, CallPolicy policy, List path) { if (path.length > maxCallNestingDepth) { @@ -182,13 +190,25 @@ class CallDecoder { input.offset += 1; return runtime.Utility(_decodeUtility(input, policy, path)); } - if (pallet == _recoveryPalletIndex) { - input.offset += 1; - return runtime.Recovery(_decodeRecovery(input, policy, path)); + if (id == CallIds.multisigExecute) { + input.offset += 2; + return runtime.Multisig(_decodeMultisigExecute(input, policy, path)); } return runtime.RuntimeCall.codec.decode(input); } + /// `multisig.execute` carries its inner call inline, so letting the generated + /// codec read it would skip the policy and nesting checks every other call + /// boundary gets. Read the fields here and route the inner call back through + /// [_decodeCall]. + static multisig.Call _decodeMultisigExecute(ByteInput input, CallPolicy policy, List path) { + return multisig.Execute( + multisigAddress: const U8ArrayCodec(32).decode(input), + proposalId: U32Codec.codec.decode(input), + call: _decodeCall(input, policy, [...path, CallIds.multisigExecute]), + ); + } + static utility.Call _decodeUtility(ByteInput input, CallPolicy policy, List path) { final variant = _readIndex(input); if (variant != _batchAllCallIndex) { @@ -200,18 +220,6 @@ class CallDecoder { return utility.BatchAll(calls: _decodeCalls(input, policy, [...path, CallIds.batchAll])); } - static recovery.Call _decodeRecovery(ByteInput input, CallPolicy policy, List path) { - final variant = _readIndex(input); - if (variant != _asRecoveredCallIndex) { - input.offset -= 1; - return recovery.Call.codec.decode(input); - } - return recovery.AsRecovered( - account: multi_address.MultiAddress.codec.decode(input), - call: _decodeCall(input, policy, [...path, CallId.wire(_recoveryPalletIndex, variant)]), - ); - } - static List _decodeCalls(ByteInput input, CallPolicy policy, List path) { final count = CompactCodec.codec.decode(input); final remaining = input.remainingLength ?? 0; @@ -369,8 +377,20 @@ class CallDecoder { ); case multisig.Cancel(:final multisigAddress, :final proposalId): return _multisigProposalRef('cancel', multisigAddress, proposalId); - case multisig.Execute(:final multisigAddress, :final proposalId): - return _multisigProposalRef('execute', multisigAddress, proposalId); + case multisig.Execute(:final multisigAddress, :final proposalId, :final call): + // The chain dispatches this only if the call re-encodes to the payload + // stored at the proposal id, so what is shown here is what executes. + final inner = _describe(call, depth + 1, policy: policy, path: [...path, CallIds.multisigExecute]); + return DecodedCall( + pallet: 'Multisig', + call: 'execute', + fields: [ + _accountField('Multisig account', multisigAddress), + ValueField('Proposal id', '$proposalId', kind: ValueKind.number), + NestedCallField('You are executing', inner), + ], + summary: inner.summary, + ); case multisig.RemoveExpired(:final multisigAddress, :final proposalId): return _multisigProposalRef('remove_expired', multisigAddress, proposalId); case multisig.ClaimDeposits(:final multisigAddress): @@ -547,7 +567,7 @@ class CallDecoder { } } - // ------------------------------------------------------- Utility / Recovery + // ----------------------------------------------------------------- Utility static DecodedCall _utility(utility.Call call, int depth, CallPolicy policy, List path) { switch (call) { @@ -576,64 +596,6 @@ class CallDecoder { ); } - static DecodedCall _recovery(recovery.Call call, int depth, CallPolicy policy, List path) { - switch (call) { - case recovery.AsRecovered(:final account, :final call): - final inner = _describe( - call, - depth + 1, - policy: policy, - path: [...path, CallId.wire(_recoveryPalletIndex, _asRecoveredCallIndex)], - ); - return DecodedCall( - pallet: 'Recovery', - call: 'as_recovered', - fields: [_addressField('Recovered account', account), NestedCallField('Call', inner)], - summary: inner.summary, - ); - case recovery.CreateRecovery(:final friends, :final threshold, :final delayPeriod): - return DecodedCall( - pallet: 'Recovery', - call: 'create_recovery', - fields: [ - _accountListField('Friends', friends), - ValueField('Threshold', '$threshold of ${friends.length}', kind: ValueKind.number), - ValueField('Waiting period', '$delayPeriod blocks', kind: ValueKind.blockOrTime), - ], - ); - case recovery.SetRecovered(:final lost, :final rescuer): - return DecodedCall( - pallet: 'Recovery', - call: 'set_recovered', - fields: [_addressField('Lost account', lost), _addressField('Rescuer', rescuer)], - ); - case recovery.VouchRecovery(:final lost, :final rescuer): - return DecodedCall( - pallet: 'Recovery', - call: 'vouch_recovery', - fields: [_addressField('Lost account', lost), _addressField('Rescuer', rescuer)], - ); - case recovery.InitiateRecovery(:final account): - return DecodedCall( - pallet: 'Recovery', - call: 'initiate_recovery', - fields: [_addressField('Account to recover', account)], - ); - case recovery.ClaimRecovery(:final account): - return DecodedCall( - pallet: 'Recovery', - call: 'claim_recovery', - fields: [_addressField('Account to claim', account)], - ); - case recovery.CloseRecovery(:final rescuer): - return DecodedCall(pallet: 'Recovery', call: 'close_recovery', fields: [_addressField('Rescuer', rescuer)]); - case recovery.CancelRecovered(:final account): - return DecodedCall(pallet: 'Recovery', call: 'cancel_recovered', fields: [_addressField('Account', account)]); - default: - return _generic(runtime.Recovery(call)); - } - } - // ----------------------------------------------------------------- Vesting static DecodedCall _vesting(vesting.Call call) { @@ -792,12 +754,10 @@ class CallDecoder { final callName = inner.keys.first; final args = inner[callName]; - final fields = []; - if (args is Map) { - args.forEach((key, value) => fields.add(_genericField(_humanLabel(key.toString()), value))); - } else if (args != null) { - fields.add(_genericField('Value', args)); - } + final fields = [ + if (args != null) + for (final arg in args.entries) _genericField(_humanLabel(arg.key), arg.value), + ]; return DecodedCall(pallet: pallet, call: callName, fields: fields); } diff --git a/quantus_sdk/lib/src/chain/call_policy.dart b/quantus_sdk/lib/src/chain/call_policy.dart index 7bb8ca7c8..09ea1bae2 100644 --- a/quantus_sdk/lib/src/chain/call_policy.dart +++ b/quantus_sdk/lib/src/chain/call_policy.dart @@ -46,6 +46,10 @@ final _zero32 = List.filled(32, 0); final _zeroDest = multi_address.MultiAddress.values.id(_zero32); final _zero = BigInt.zero; +/// Stands in for the inner call of a sample `multisig.execute`, which carries one +/// inline. Only the enclosing call's own indices are read off the sample. +final _zeroInnerCall = const balances_pallet.Txs().transferAllowDeath(dest: _zeroDest, value: _zero); + /// The calls the wallet grammar names, each read off the runtime's own encoder. class CallIds { const CallIds._(); @@ -88,7 +92,7 @@ class CallIds { ); static final multisigCancel = CallId.of(const multisig_pallet.Txs().cancel(multisigAddress: _zero32, proposalId: 0)); static final multisigExecute = CallId.of( - const multisig_pallet.Txs().execute(multisigAddress: _zero32, proposalId: 0), + const multisig_pallet.Txs().execute(multisigAddress: _zero32, proposalId: 0, call: _zeroInnerCall), ); static final removeExpired = CallId.of( const multisig_pallet.Txs().removeExpired(multisigAddress: _zero32, proposalId: 0), @@ -170,12 +174,20 @@ class WalletCallPolicy extends CallPolicy { CallIds.claimDeposits, }; + /// The multisig calls that carry a proposal's inner call. Each is bound by the + /// chain to the stored payload, so what they nest is what the proposal holds. + static final Set _carriesProposal = { + CallIds.multisigPropose, + CallIds.multisigApprove, + CallIds.multisigExecute, + }; + @override bool allows(CallId id, List path) { if (path.isEmpty) return _topLevel.contains(id); final parent = path.last; if (parent == CallIds.batchAll) return CallIds.transfers.contains(id); - if (parent == CallIds.multisigPropose || parent == CallIds.multisigApprove) return _proposalInner.contains(id); + if (_carriesProposal.contains(parent)) return _proposalInner.contains(id); return false; } } diff --git a/quantus_sdk/lib/src/constants/app_constants.dart b/quantus_sdk/lib/src/constants/app_constants.dart index d79a5024c..c1540c367 100644 --- a/quantus_sdk/lib/src/constants/app_constants.dart +++ b/quantus_sdk/lib/src/constants/app_constants.dart @@ -69,8 +69,8 @@ class AppConstants { // from. A signing payload declaring a different spec version may decode // against shifted pallet/call indices, so signers warn loudly rather than // present a decode they cannot vouch for. Bump both when regenerating. - static const int bundledSpecVersion = 146; - static const int bundledTransactionVersion = 5; + static const int bundledSpecVersion = 147; + static const int bundledTransactionVersion = 6; // Runtimes this build's metadata decodes correctly, beyond the one it was // generated from. A pair belongs here only once the calls the wallet displays diff --git a/quantus_sdk/lib/src/services/multisig_service.dart b/quantus_sdk/lib/src/services/multisig_service.dart index aed325d68..0e15c1a29 100644 --- a/quantus_sdk/lib/src/services/multisig_service.dart +++ b/quantus_sdk/lib/src/services/multisig_service.dart @@ -4,6 +4,8 @@ import 'package:flutter/foundation.dart'; import 'package:quantus_sdk/generated/planck/pallets/multisig.dart' show Constants, Txs; import 'package:quantus_sdk/generated/planck/planck.dart' show Planck; import 'package:quantus_sdk/generated/planck/types/quantus_runtime/runtime_call.dart'; +import 'package:quantus_sdk/src/chain/call_decoder.dart'; +import 'package:quantus_sdk/src/chain/call_policy.dart'; import 'package:quantus_sdk/src/constants/app_constants.dart'; import 'package:quantus_sdk/src/models/account.dart'; import 'package:quantus_sdk/src/models/json_dynamic_parse.dart'; @@ -452,17 +454,36 @@ class MultisigService { } /// Builds the `multisig.execute` runtime call for [proposalId]. - Multisig buildExecuteCall({required MultisigAccount msig, required int proposalId}) { - return const Txs().execute(multisigAddress: getAccountId32(msig.accountId), proposalId: proposalId); + /// + /// [call] must be the proposal's stored inner call bytes — see + /// [fetchProposalCallBytes]. The chain dispatches only a call that re-encodes + /// to them, which is what lets the executor read what they are dispatching + /// instead of signing an opaque proposal id. + /// + /// Unlike `approve`, which resubmits the bytes length-prefixed, `execute` + /// carries the call itself, so the stored bytes are decoded here. A proposal + /// the bundled metadata cannot read fails now, before signing, rather than + /// building a call the chain would reject. + Multisig buildExecuteCall({required MultisigAccount msig, required int proposalId, required List call}) { + return const Txs().execute( + multisigAddress: getAccountId32(msig.accountId), + proposalId: proposalId, + call: CallDecoder.decodeRuntimeCall(call, policy: const WalletCallPolicy(), within: CallIds.insideProposal), + ); } /// Estimates the network fee for executing [proposalId]. + /// + /// Fee scales with the inner call size, so [callBytes] is fetched when not + /// supplied by the caller. Future estimateExecuteFee({ required MultisigAccount msig, required Account signer, required int proposalId, + List? callBytes, }) async { - final call = buildExecuteCall(msig: msig, proposalId: proposalId); + final inner = callBytes ?? await fetchProposalCallBytes(msig: msig, proposalId: proposalId); + final call = buildExecuteCall(msig: msig, proposalId: proposalId, call: inner); final feeData = await _substrateService.getFeeForCall(signer, call); return feeData.fee; } @@ -472,8 +493,10 @@ class MultisigService { required MultisigAccount msig, required Account signer, required int proposalId, + List? callBytes, }) async { - final call = buildExecuteCall(msig: msig, proposalId: proposalId); + final inner = callBytes ?? await fetchProposalCallBytes(msig: msig, proposalId: proposalId); + final call = buildExecuteCall(msig: msig, proposalId: proposalId, call: inner); return _substrateService.submitExtrinsic(signer, call); } diff --git a/quantus_sdk/lib/src/services/recovery_service.dart b/quantus_sdk/lib/src/services/recovery_service.dart deleted file mode 100644 index 6284443d1..000000000 --- a/quantus_sdk/lib/src/services/recovery_service.dart +++ /dev/null @@ -1,309 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:quantus_sdk/generated/planck/planck.dart'; -import 'package:quantus_sdk/generated/planck/types/pallet_recovery/active_recovery.dart'; -import 'package:quantus_sdk/generated/planck/types/pallet_recovery/recovery_config.dart'; -import 'package:quantus_sdk/generated/planck/types/sp_runtime/multiaddress/multi_address.dart' as multi_address; -import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:quantus_sdk/src/rust/api/crypto.dart' as crypto; - -/// Service for managing account recovery functionality -class RecoveryService { - static final RecoveryService _instance = RecoveryService._internal(); - factory RecoveryService() => _instance; - RecoveryService._internal(); - - final SubstrateService _substrateService = SubstrateService(); - - final dummyQuantusApi = Planck.url(Uri.parse(AppConstants.rpcEndpoints[0])); - late final BigInt configDepositBase = dummyQuantusApi.constant.recovery.configDepositBase; - late final BigInt friendDepositFactor = dummyQuantusApi.constant.recovery.friendDepositFactor; - late final int maxFriends = dummyQuantusApi.constant.recovery.maxFriends; - late final BigInt recoveryDeposit = dummyQuantusApi.constant.recovery.recoveryDeposit; - - /// Create a recovery configuration for an account - /// This makes the account recoverable by trusted friends - Future createRecoveryConfig({ - required Account account, - required List friendAddresses, - required int threshold, - required int delayPeriod, - }) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final friends = friendAddresses.map((addr) => crypto.ss58ToAccountId(s: addr)).toList(); - - // Create the call - final call = quantusApi.tx.recovery.createRecovery( - friends: friends, - threshold: threshold, - delayPeriod: delayPeriod, - ); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(account, call); - } catch (e) { - throw Exception('Failed to create recovery config: $e'); - } - } - - /// Initiate recovery process for a lost account - Future initiateRecovery({required Account rescuerAccount, required String lostAccountAddress}) async { - try { - final call = getInitiateRecoveryCall(lostAccountAddress); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(rescuerAccount, call); - } catch (e) { - throw Exception('Failed to initiate recovery: $e'); - } - } - - RuntimeCall getInitiateRecoveryCall(String lostAccountAddress) { - final quantusApi = Planck(_substrateService.provider!); - final lostAccount = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: lostAccountAddress)); - return quantusApi.tx.recovery.initiateRecovery(account: lostAccount); - } - - /// Vouch for an active recovery process (called by friends) - Future vouchForRecovery({ - required Account friendAccount, - required String lostAccountAddress, - required String rescuerAddress, - }) async { - try { - final call = getVouchRecoveryCall(lostAccountAddress, rescuerAddress); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(friendAccount, call); - } catch (e) { - throw Exception('Failed to vouch for recovery: $e'); - } - } - - RuntimeCall getVouchRecoveryCall(String lostAccountAddress, String rescuerAddress) { - final quantusApi = Planck(_substrateService.provider!); - final lostAccount = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: lostAccountAddress)); - final rescuer = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: rescuerAddress)); - return quantusApi.tx.recovery.vouchRecovery(lost: lostAccount, rescuer: rescuer); - } - - /// Claim recovery of a lost account (called by rescuer after threshold is met) - Future claimRecovery({required Account rescuerAccount, required String lostAccountAddress}) async { - try { - final call = getClaimRecoveryCall(lostAccountAddress); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(rescuerAccount, call); - } catch (e) { - throw Exception('Failed to claim recovery: $e'); - } - } - - RuntimeCall getClaimRecoveryCall(String lostAccountAddress) { - final quantusApi = Planck(_substrateService.provider!); - final lostAccount = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: lostAccountAddress)); - return quantusApi.tx.recovery.claimRecovery(account: lostAccount); - } - - /// Close an active recovery process (called by the lost account owner) - Future closeRecovery({required Account lostAccount, required String rescuerAddress}) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final rescuer = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: rescuerAddress)); - - // Create the call - final call = quantusApi.tx.recovery.closeRecovery(rescuer: rescuer); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(lostAccount, call); - } catch (e) { - throw Exception('Failed to close recovery: $e'); - } - } - - /// Remove recovery configuration from account - Future removeRecoveryConfig({required Account senderAccount}) async { - try { - final quantusApi = Planck(_substrateService.provider!); - - // Create the call - final call = quantusApi.tx.recovery.removeRecovery(); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(senderAccount, call); - } catch (e) { - throw Exception('Failed to remove recovery config: $e'); - } - } - - /// Call a function as a recovered account (proxy call) - Future callAsRecovered({ - required Account rescuerAccount, - required String recoveredAccountAddress, - required RuntimeCall call, - }) async { - try { - final proxyCall = getAsRecoveredCall(recoveredAccountAddress, call); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(rescuerAccount, proxyCall); - } catch (e) { - throw Exception('Failed to call as recovered: $e'); - } - } - - RuntimeCall getAsRecoveredCall(String recoveredAccountAddress, RuntimeCall call) { - final quantusApi = Planck(_substrateService.provider!); - final recoveredAccount = const multi_address.$MultiAddress().id(crypto.ss58ToAccountId(s: recoveredAccountAddress)); - return quantusApi.tx.recovery.asRecovered(account: recoveredAccount, call: call); - } - - /// Cancel the ability to use a recovered account - Future cancelRecovered({required Account rescuerAccount, required String recoveredAccountAddress}) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final recoveredAccount = const multi_address.$MultiAddress().id( - crypto.ss58ToAccountId(s: recoveredAccountAddress), - ); - - // Create the call - final call = quantusApi.tx.recovery.cancelRecovered(account: recoveredAccount); - - // Submit the transaction using substrate service - return await _substrateService.submitExtrinsic(rescuerAccount, call); - } catch (e) { - throw Exception('Failed to cancel recovered: $e'); - } - } - - /// Query recovery configuration for an account - Future getRecoveryConfig(String address) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final accountId = crypto.ss58ToAccountId(s: address); - - return await quantusApi.query.recovery.recoverable(accountId); - } catch (e) { - throw Exception('Failed to get recovery config: $e'); - } - } - - /// Query active recovery process - Future getActiveRecovery(String lostAccountAddress, String rescuerAddress) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final lostAccountId = crypto.ss58ToAccountId(s: lostAccountAddress); - final rescuerId = crypto.ss58ToAccountId(s: rescuerAddress); - - return await quantusApi.query.recovery.activeRecoveries(lostAccountId, rescuerId); - } catch (e) { - throw Exception('Failed to get active recovery: $e'); - } - } - - /// Check if an account can act as proxy for a recovered account - Future getProxyRecoveredAccount(String proxyAddress) async { - try { - final quantusApi = Planck(_substrateService.provider!); - final proxyId = crypto.ss58ToAccountId(s: proxyAddress); - - final recoveredAccountId = await quantusApi.query.recovery.proxy(proxyId); - // The storage map returns the final AccountId32, so encode it directly to - // SS58. crypto.toAccountId would incorrectly Poseidon-hash the - // already-derived account ID. - return recoveredAccountId != null - ? AddressExtension.ss58AddressFromBytes(Uint8List.fromList(recoveredAccountId)) - : null; - } catch (e) { - throw Exception('Failed to get proxy recovered account: $e'); - } - } - - /// Check if account has recovery configuration - Future hasRecoveryConfig(String address) async { - try { - final config = await getRecoveryConfig(address); - return config != null; - } catch (e) { - throw Exception('Failed to check recovery config: $e'); - } - } - - /// Check if recovery process is active - Future isRecoveryActive(String lostAccountAddress, String rescuerAddress) async { - try { - final activeRecovery = await getActiveRecovery(lostAccountAddress, rescuerAddress); - return activeRecovery != null; - } catch (e) { - throw Exception('Failed to check recovery status: $e'); - } - } - - /// Get recovery progress (how many vouches received vs threshold) - Future> getRecoveryProgress(String lostAccountAddress, String rescuerAddress) async { - try { - final activeRecovery = await getActiveRecovery(lostAccountAddress, rescuerAddress); - final config = await getRecoveryConfig(lostAccountAddress); - - if (activeRecovery == null || config == null) { - throw Exception('No active recovery or config found'); - } - - return { - 'vouches': activeRecovery.friends.length, - 'threshold': config.threshold, - 'delayPeriod': config.delayPeriod, - 'created': activeRecovery.created, - }; - } catch (e) { - throw Exception('Failed to get recovery progress: $e'); - } - } - - /// Get recovery constants - Future> getConstants() async { - try { - final quantusApi = Planck(_substrateService.provider!); - final constants = quantusApi.constant.recovery; - - return { - 'configDepositBase': constants.configDepositBase, - 'friendDepositFactor': constants.friendDepositFactor, - 'maxFriends': constants.maxFriends, - 'recoveryDeposit': constants.recoveryDeposit, - }; - } catch (e) { - throw Exception('Failed to get recovery constants: $e'); - } - } - - /// Helper to create a balance transfer call for recovered account - Balances createBalanceTransferCall(String recipientAddress, BigInt amount) { - final quantusApi = Planck(_substrateService.provider!); - final accountID = crypto.ss58ToAccountId(s: recipientAddress); - final dest = const multi_address.$MultiAddress().id(accountID); - final call = quantusApi.tx.balances.transferAllowDeath(dest: dest, value: amount); - return call; - } - - /// Convenience method to transfer balance as recovered account - Future transferAsRecovered({ - required Account rescuerAccount, - required String recoveredAccountAddress, - required String recipientAddress, - required BigInt amount, - }) async { - try { - final transferCall = createBalanceTransferCall(recipientAddress, amount); - return await callAsRecovered( - rescuerAccount: rescuerAccount, - recoveredAccountAddress: recoveredAccountAddress, - call: transferCall, - ); - } catch (e) { - throw Exception('Failed to transfer as recovered: $e'); - } - } -} diff --git a/quantus_sdk/lib/src/testing/call_corpus.dart b/quantus_sdk/lib/src/testing/call_corpus.dart index 8e4f2232f..b68685a7f 100644 --- a/quantus_sdk/lib/src/testing/call_corpus.dart +++ b/quantus_sdk/lib/src/testing/call_corpus.dart @@ -67,42 +67,6 @@ const Map callCorpus = { 'TechReferenda.set_metadata [maybe_hash=Some]': '0e0843420f0001a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', 'TreasuryPallet.set_treasury_account': '0f00a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.as_recovered': '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Timestamp]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Balances]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Preimage]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Utility]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=ReversibleTransfers]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=TechCollective]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=TechReferenda]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=TreasuryPallet]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Recovery]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Multisig]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Wormhole]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [call=Vesting]': - '100000a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.set_recovered': - '100100a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.create_recovery': '100204a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0921045420f00', - 'Recovery.initiate_recovery': '100300a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.vouch_recovery': - '100400a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.claim_recovery': '100500a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.close_recovery': '100600a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.remove_recovery': '1007', - 'Recovery.cancel_recovered': '100800a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.poke_deposit': '100900', 'Multisig.create_multisig': '130004a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f000098f73e5d010000', 'Multisig.propose': '1301a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a00c00000045420f00', @@ -110,7 +74,29 @@ const Map callCorpus = { 'Multisig.cancel': '1303a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00', 'Multisig.remove_expired': '1304a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00', 'Multisig.claim_deposits': '1305a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Multisig.execute': '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00', + 'Multisig.execute': '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Timestamp]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Balances]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Preimage]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Utility]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=ReversibleTransfers]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=TechCollective]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=TechReferenda]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=TreasuryPallet]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Multisig]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Wormhole]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', + 'Multisig.execute [call=Vesting]': + '1306a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a044420f00000000', 'Wormhole.verify_private_batch': '14020c000000', 'Wormhole.verify_public_batch': '14030c000000', 'Vesting.claim': '16000098f73e5d010000', @@ -185,58 +171,4 @@ const Map refusedCallCorpus = { '0d0600a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a003a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', 'TechCollective.exchange_member [new_who=Address20]': '0d0600a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.as_recovered [account=Index]': '100001000000', - 'Recovery.as_recovered [account=Raw]': '1000020c000000000000', - 'Recovery.as_recovered [account=Address32]': - '100003a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.as_recovered [account=Address20]': '100004a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0000000', - 'Recovery.set_recovered [lost=Index]': '10010100a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.set_recovered [lost=Raw]': - '1001020c00000000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.set_recovered [lost=Address32]': - '100103a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.set_recovered [lost=Address20]': - '100104a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.set_recovered [rescuer=Index]': '100100a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a001', - 'Recovery.set_recovered [rescuer=Raw]': - '100100a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0020c000000', - 'Recovery.set_recovered [rescuer=Address32]': - '100100a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a003a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.set_recovered [rescuer=Address20]': - '100100a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.initiate_recovery [account=Index]': '100301', - 'Recovery.initiate_recovery [account=Raw]': '1003020c000000', - 'Recovery.initiate_recovery [account=Address32]': - '100303a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.initiate_recovery [account=Address20]': '100304a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.vouch_recovery [lost=Index]': '10040100a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.vouch_recovery [lost=Raw]': - '1004020c00000000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.vouch_recovery [lost=Address32]': - '100403a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.vouch_recovery [lost=Address20]': - '100404a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a000a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.vouch_recovery [rescuer=Index]': '100400a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a001', - 'Recovery.vouch_recovery [rescuer=Raw]': - '100400a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0020c000000', - 'Recovery.vouch_recovery [rescuer=Address32]': - '100400a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a003a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.vouch_recovery [rescuer=Address20]': - '100400a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1', - 'Recovery.claim_recovery [account=Index]': '100501', - 'Recovery.claim_recovery [account=Raw]': '1005020c000000', - 'Recovery.claim_recovery [account=Address32]': - '100503a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.claim_recovery [account=Address20]': '100504a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.close_recovery [rescuer=Index]': '100601', - 'Recovery.close_recovery [rescuer=Raw]': '1006020c000000', - 'Recovery.close_recovery [rescuer=Address32]': - '100603a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.close_recovery [rescuer=Address20]': '100604a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.cancel_recovered [account=Index]': '100801', - 'Recovery.cancel_recovered [account=Raw]': '1008020c000000', - 'Recovery.cancel_recovered [account=Address32]': - '100803a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.cancel_recovered [account=Address20]': '100804a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0', - 'Recovery.poke_deposit [maybe_account=Some]': '10090101', }; diff --git a/quantus_sdk/pubspec.yaml b/quantus_sdk/pubspec.yaml index 8ac9a27e1..65a8dfdd8 100644 --- a/quantus_sdk/pubspec.yaml +++ b/quantus_sdk/pubspec.yaml @@ -19,6 +19,14 @@ dependencies: # Shared (canonical versions in melos.yaml) # DO NOT UPDATE polkadart - newer versions are incompatible with our ML-DSA # signature override, work completely differently, and add no benefits. + # + # The fork also carries a security fix to the code generator. Upstream emits a + # compact integer for the MultiAddress::Index variant where the metadata + # declares the field zero-width. A codec that disagrees with the metadata about + # a field's width re-frames every byte after it, so a crafted payload can + # display one call and sign another - a clearsigning bypass on the cold wallet. + # Pinned by test/chain/multi_address_codec_test.dart. Never regenerate the + # bindings with pub.dev polkadart_cli. # Re-exported from quantus_sdk.dart so apps don't need a direct polkadart dep. polkadart: git: @@ -71,7 +79,8 @@ dev_dependencies: url: https://github.com/Quantus-Network/polkadart.git ref: 0_7_3_quantus_2 path: packages/substrate_metadata - # DO NOT UPDATE polkadart_cli - must match polkadart 0.7.x. See note above. + # DO NOT UPDATE polkadart_cli - must match polkadart 0.7.x, and generates the + # MultiAddress::Index codec correctly. See note above. polkadart_cli: git: url: https://github.com/Quantus-Network/polkadart.git diff --git a/quantus_sdk/test/chain/call_decoder_test.dart b/quantus_sdk/test/chain/call_decoder_test.dart index 85b313a4e..546a1dd29 100644 --- a/quantus_sdk/test/chain/call_decoder_test.dart +++ b/quantus_sdk/test/chain/call_decoder_test.dart @@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:quantus_sdk/generated/planck/pallets/balances.dart' as balances_pallet; import 'package:quantus_sdk/generated/planck/pallets/multisig.dart' as multisig_pallet; import 'package:quantus_sdk/generated/planck/pallets/preimage.dart' as preimage_pallet; -import 'package:quantus_sdk/generated/planck/pallets/recovery.dart' as recovery_pallet; import 'package:quantus_sdk/generated/planck/pallets/reversible_transfers.dart' as reversible_pallet; import 'package:quantus_sdk/generated/planck/pallets/system.dart' as system_pallet; import 'package:quantus_sdk/generated/planck/pallets/tech_collective.dart' as collective_pallet; @@ -37,26 +36,26 @@ final oneToken = BigInt.from(1000000000000); /// runs against the same path a signer takes: bytes in, display tree out. DecodedCall roundTrip(RuntimeCall call) => CallDecoder.decodeBytes(call.encode(), policy: const FullCallPolicy()); -RuntimeCall nestedRecovered(int depth) { +RuntimeCall nestedExecute(int depth) { RuntimeCall call = const system_pallet.Txs().remark(remark: []); for (var i = 0; i < depth; i++) { - call = const recovery_pallet.Txs().asRecovered(account: dest(aliceId), call: call); + call = const multisig_pallet.Txs().execute(multisigAddress: aliceId, proposalId: 4, call: call); } return call; } RuntimeCall multisigWrapping(int depth) => - const multisig_pallet.Txs().propose(multisigAddress: aliceId, call: nestedRecovered(depth).encode(), expiry: 10); + const multisig_pallet.Txs().propose(multisigAddress: aliceId, call: nestedExecute(depth).encode(), expiry: 10); -RuntimeCall recoveryWrapping(int depth) => - const recovery_pallet.Txs().asRecovered(account: dest(aliceId), call: nestedRecovered(depth)); +RuntimeCall executeWrapping(int depth) => + const multisig_pallet.Txs().execute(multisigAddress: aliceId, proposalId: 4, call: nestedExecute(depth)); RuntimeCall preimageWrapping(int depth) => - const preimage_pallet.Txs().notePreimage(bytes: nestedRecovered(depth).encode()); + const preimage_pallet.Txs().notePreimage(bytes: nestedExecute(depth).encode()); RuntimeCall referendaWrapping(int depth) => const referenda_pallet.Txs().submit( proposalOrigin: rootOrigin, - proposal: bounded.Bounded.values.inline(nestedRecovered(depth).encode()), + proposal: bounded.Bounded.values.inline(nestedExecute(depth).encode()), enactmentMoment: dispatch_time.DispatchTime.values.after(100), ); @@ -65,13 +64,20 @@ final rootOrigin = origin_caller.OriginCaller.values.system(raw_origin.RawOrigin /// Built straight from bytes: encoding a chain this deep in Dart would itself /// blow the stack, which is the point — a decoder that recurses first never gets /// to say no. -final int _recoveryPallet = const recovery_pallet.Txs().removeRecovery().encode()[0]; - -Uint8List recoveredChainBytes(int depth) => Uint8List.fromList([ - for (var i = 0; i < depth; i++) ...[_recoveryPallet, 0, 0, ...List.filled(32, 0xAA)], +Uint8List executeChainBytes(int depth) => Uint8List.fromList([ + for (var i = 0; i < depth; i++) ...[ + CallIds.multisigExecute.pallet, + CallIds.multisigExecute.call, + ...List.filled(32, 0xAA), // multisig address + 0, 0, 0, 0, // proposal id + ], 0, 0, 0, // System(0) · remark(0), empty ]); +/// Bytes one level of [executeChainBytes] costs: the two indices, the address +/// and the proposal id. +const int _executeLevelBytes = 2 + 32 + 4; + /// Every field of a decoded tree as one comparable string, so a decode that got /// a variant index or a field order wrong cannot compare equal. String flatten(DecodedCall call) => '${call.pallet}.${call.call}(${call.fields.map(flattenField).join(', ')})'; @@ -211,11 +217,8 @@ void main() { expect(valueField(decoded, 'Threshold').value, '2 of 2'); }); - test('execute and cancel flag that the proposal contents are not in the payload', () { - for (final decoded in [ - roundTrip(const multisig_pallet.Txs().execute(multisigAddress: aliceId, proposalId: 4)), - roundTrip(const multisig_pallet.Txs().cancel(multisigAddress: aliceId, proposalId: 4)), - ]) { + test('cancel flags that the proposal contents are not in the payload', () { + for (final decoded in [roundTrip(const multisig_pallet.Txs().cancel(multisigAddress: aliceId, proposalId: 4))]) { final field = valueField(decoded, 'Proposal id'); expect(field.value, '4'); expect(field.note, contains('not part of what you sign')); @@ -309,14 +312,14 @@ void main() { group('nested and batched calls', () { test('allows top-level calls and two nested levels', () { for (final depth in [0, 1, 2]) { - final decoded = roundTrip(nestedRecovered(depth)); - expect(decoded.call, depth == 0 ? 'remark' : 'as_recovered'); + final decoded = roundTrip(nestedExecute(depth)); + expect(decoded.call, depth == 0 ? 'remark' : 'execute'); } }); test('rejects three or more nested inline levels', () { for (final depth in [3, 4, 42]) { - expectNestingRejected(nestedRecovered(depth)); + expectNestingRejected(nestedExecute(depth)); } }); @@ -329,8 +332,8 @@ void main() { }); test('applies the limit to utility batches', () { - roundTrip(const utility_pallet.Txs().batchAll(calls: [nestedRecovered(1)])); - expectNestingRejected(const utility_pallet.Txs().batchAll(calls: [nestedRecovered(2)])); + roundTrip(const utility_pallet.Txs().batchAll(calls: [nestedExecute(1)])); + expectNestingRejected(const utility_pallet.Txs().batchAll(calls: [nestedExecute(2)])); }); test('propagates the limit through multisig proposal bytes', () { @@ -338,9 +341,9 @@ void main() { expectNestingRejected(multisigWrapping(2)); }); - test('propagates the limit through recovery-wrapped calls', () { - roundTrip(recoveryWrapping(1)); - expectNestingRejected(recoveryWrapping(2)); + test('propagates the limit through the call multisig.execute carries', () { + roundTrip(executeWrapping(1)); + expectNestingRejected(executeWrapping(2)); }); test('propagates the limit through inline referendum proposals', () { @@ -354,12 +357,12 @@ void main() { }); test('a deeply nested chain the size of the report payload is rejected', () { - expectNestingRejected(nestedRecovered(42)); + expectNestingRejected(nestedExecute(42)); }); test('rejects over-nested bytes without recursing into them', () { - final depth = (maxCallBytes - 3) ~/ 35; - final bytes = recoveredChainBytes(depth); + final depth = (maxCallBytes - 3) ~/ _executeLevelBytes; + final bytes = executeChainBytes(depth); expect(bytes.length, lessThanOrEqualTo(maxCallBytes)); expect(() => CallDecoder.decodeBytes(bytes, policy: const FullCallPolicy()), isNestingRejection); }); @@ -370,10 +373,10 @@ void main() { final variants = [ const utility_pallet.Txs().batchAll(calls: [inner, other]), const utility_pallet.Txs().batchAll(calls: []), - const recovery_pallet.Txs().asRecovered(account: dest(aliceId), call: inner), - // Recovery variants the bounded decoder must hand back to the codec. - const recovery_pallet.Txs().claimRecovery(account: dest(bobId)), - const recovery_pallet.Txs().removeRecovery(), + const multisig_pallet.Txs().execute(multisigAddress: aliceId, proposalId: 4, call: inner), + // Multisig variants the bounded decoder must hand back to the codec. + const multisig_pallet.Txs().cancel(multisigAddress: bobId, proposalId: 4), + const multisig_pallet.Txs().claimDeposits(multisigAddress: bobId), ]; for (final call in variants) { expect( @@ -400,16 +403,17 @@ void main() { expect(nestedField(decoded, 'Call 2').call.call, 'vote'); }); - test('recovery.as_recovered lifts the wrapped transfer summary', () { + test('multisig.execute lifts the carried transfer summary', () { final decoded = roundTrip( - const recovery_pallet.Txs().asRecovered( - account: dest(aliceId), + const multisig_pallet.Txs().execute( + multisigAddress: aliceId, + proposalId: 4, call: const balances_pallet.Txs().transferAllowDeath(dest: dest(bobId), value: oneToken), ), ); - expect(decoded.call, 'as_recovered'); - expect(nestedField(decoded, 'Call').call.call, 'transfer_allow_death'); + expect(decoded.call, 'execute'); + expect(nestedField(decoded, 'You are executing').call.call, 'transfer_allow_death'); expect(decoded.summary?.amount, oneToken); }); }); diff --git a/quantus_sdk/test/chain/call_policy_test.dart b/quantus_sdk/test/chain/call_policy_test.dart index 6f5497ccc..dd31a91b4 100644 --- a/quantus_sdk/test/chain/call_policy_test.dart +++ b/quantus_sdk/test/chain/call_policy_test.dart @@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:quantus_sdk/generated/planck/pallets/balances.dart' as balances_pallet; import 'package:quantus_sdk/generated/planck/pallets/multisig.dart' as multisig_pallet; import 'package:quantus_sdk/generated/planck/pallets/preimage.dart' as preimage_pallet; -import 'package:quantus_sdk/generated/planck/pallets/recovery.dart' as recovery_pallet; import 'package:quantus_sdk/generated/planck/pallets/reversible_transfers.dart' as reversible_pallet; import 'package:quantus_sdk/generated/planck/pallets/system.dart' as system_pallet; import 'package:quantus_sdk/generated/planck/pallets/tech_collective.dart' as collective_pallet; @@ -57,7 +56,7 @@ void main() { const preimage_pallet.Txs().notePreimage(bytes: [0, 0, 0]), const system_pallet.Txs().remark(remark: [1, 2, 3]), const vesting_pallet.Txs().claim(scheduleId: BigInt.one), - const recovery_pallet.Txs().removeRecovery(), + const balances_pallet.Txs().burn(value: _one, keepAlive: true), const utility_pallet.Txs().batchAll(calls: [transfer()]), ]; @@ -78,7 +77,7 @@ void main() { test('accepts the multisig lifecycle', () { expectAllowed(const multisig_pallet.Txs().cancel(multisigAddress: _alice, proposalId: 1)); - expectAllowed(const multisig_pallet.Txs().execute(multisigAddress: _alice, proposalId: 1)); + expectAllowed(const multisig_pallet.Txs().execute(multisigAddress: _alice, proposalId: 1, call: transfer())); expectAllowed(const multisig_pallet.Txs().claimDeposits(multisigAddress: _alice)); }); @@ -87,7 +86,7 @@ void main() { expectRejected(const system_pallet.Txs().remark(remark: [1])); expectRejected(const preimage_pallet.Txs().notePreimage(bytes: [0, 0, 0])); expectRejected(const vesting_pallet.Txs().claim(scheduleId: BigInt.one)); - expectRejected(const recovery_pallet.Txs().removeRecovery()); + expectRejected(const balances_pallet.Txs().burn(value: _one, keepAlive: true)); }); test('rejects a bare batch_all outside a proposal', () { diff --git a/quantus_sdk/test/fixtures/planck_metadata.scale b/quantus_sdk/test/fixtures/planck_metadata.scale index dc346c3a6a8732026ae1abe9e66ba8f8ca1e69d4..89a95019237f64277020813f0647e99a987ef0dd 100644 GIT binary patch delta 7192 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 R-dV{H;??L@>G(G3{{b3?ECT=l delta 13806 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, with no length prefix. + final innerCall = const balances_pallet.Txs().transferAllowDeath( + dest: MultiAddress.values.id(Uint8List.fromList(List.filled(32, 0xBB))), + value: BigInt.from(900000000000), + ); + test('returns a Multisig runtime call for valid params', () { - final call = MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 3); + final call = MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 3, call: innerCall.encode()); expect(call.encode().isNotEmpty, isTrue); }); + + test('round-trips the stored call bytes and proposal id', () { + final innerBytes = innerCall.encode(); + final execute = MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 7, call: innerBytes); + + final decoded = RuntimeCall.codec.decode(Input.fromBytes(execute.encode())); + final executeCall = (decoded as Multisig).value0 as Execute; + + expect(executeCall.proposalId, 7); + expect(executeCall.call.encode(), innerBytes); + expect(CallDecoder.describe(decoded, policy: const FullCallPolicy()).summary?.amount, BigInt.from(900000000000)); + }); + + test('encodes the call inline, without the length prefix approve carries', () { + final innerBytes = innerCall.encode(); + final msig = _buildTestMsig(); + final execute = MultisigService().buildExecuteCall(msig: msig, proposalId: 7, call: innerBytes); + final approve = MultisigService().buildApproveCall(msig: msig, proposalId: 7, call: innerBytes); + + // Same fields either way, so the whole difference is approve's compact + // length prefix in front of the same bytes. + final prefix = CompactCodec.codec.encode(innerBytes.length).length; + expect(approve.encode().length - execute.encode().length, prefix); + }); + + test('refuses bytes the bundled metadata cannot decode', () { + expect( + () => MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 1, call: [0xfa, 0x00]), + throwsA(isA()), + ); + }); + + test('refuses bytes with a trailing byte after a complete call', () { + expect( + () => + MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 1, call: [...innerCall.encode(), 0]), + throwsA(isA()), + ); + }); + + test('refuses a call the wallet does not display inside a proposal', () { + final notInAProposal = const balances_pallet.Txs().burn(value: BigInt.one, keepAlive: true); + expect( + () => MultisigService().buildExecuteCall(msig: _buildTestMsig(), proposalId: 1, call: notInAProposal.encode()), + throwsA(isA()), + ); + }); }); group('MultisigService.buildCancelCall', () { diff --git a/quantus_sdk/test/quantus_payload_parser_test.dart b/quantus_sdk/test/quantus_payload_parser_test.dart index 3e05de1c0..0812f06d7 100644 --- a/quantus_sdk/test/quantus_payload_parser_test.dart +++ b/quantus_sdk/test/quantus_payload_parser_test.dart @@ -6,7 +6,6 @@ import 'package:polkadart/scale_codec.dart'; import 'package:quantus_sdk/generated/planck/pallets/balances.dart' as balances_pallet; import 'package:quantus_sdk/generated/planck/pallets/multisig.dart' as multisig_pallet; import 'package:quantus_sdk/generated/planck/pallets/preimage.dart' as preimage_pallet; -import 'package:quantus_sdk/generated/planck/pallets/recovery.dart' as recovery_pallet; import 'package:quantus_sdk/generated/planck/pallets/system.dart' as system_pallet; import 'package:quantus_sdk/generated/planck/pallets/tech_collective.dart' as collective_pallet; import 'package:quantus_sdk/generated/planck/types/sp_runtime/multiaddress/multi_address.dart' as multi_address; @@ -299,8 +298,9 @@ void main() { test('rejects a call nested deeper than the limit', () { RuntimeCall nested = const system_pallet.Txs().remark(remark: []); for (var i = 0; i < 3; i++) { - nested = const recovery_pallet.Txs().asRecovered( - account: multi_address.MultiAddress.values.id(Uint8List.fromList(List.filled(32, 0xAA))), + nested = const multisig_pallet.Txs().execute( + multisigAddress: Uint8List.fromList(List.filled(32, 0xAA)), + proposalId: 4, call: nested, ); } @@ -311,12 +311,16 @@ void main() { }); test('rejects the deepest chain the payload cap can carry', () { - // `Recovery.as_recovered` costs 35 bytes a level, so the 8 KiB cap still + // `Multisig.execute` costs 38 bytes a level, so the 8 KiB cap still // leaves room for far more levels than the depth limit allows: the cap is // not the depth limit. - final recoveryPallet = const recovery_pallet.Txs().removeRecovery().encode()[0]; final chain = [ - for (var i = 0; i < 200; i++) ...[recoveryPallet, 0, 0, ...List.filled(32, 0xAA)], + for (var i = 0; i < 200; i++) ...[ + CallIds.multisigExecute.pallet, + CallIds.multisigExecute.call, + ...List.filled(32, 0xAA), // multisig address + 0, 0, 0, 0, // proposal id + ], 0, 0, 0, // System(0) · remark(0), empty ]; final payload = Uint8List.fromList([...chain, ...extSuffix()]); diff --git a/quantus_sdk/test/services/transaction_fee_test.dart b/quantus_sdk/test/services/transaction_fee_test.dart index 243d11d2d..36d417097 100644 --- a/quantus_sdk/test/services/transaction_fee_test.dart +++ b/quantus_sdk/test/services/transaction_fee_test.dart @@ -7,14 +7,19 @@ import 'package:quantus_sdk/quantus_sdk.dart'; /// `payment_queryInfo` on a1-planck (spec 144) for a dummy-signed /// `transfer_allow_death` of 10 QUAN with a 1-byte nonce: 7303 bytes, /// weight.ref_time 5_551_728_000, partialFee 12_962_885_000. +/// +/// The bundled metadata is spec 147, which raises the normal-class base +/// extrinsic weight from 108_157_000 to 767_297_000, so the fee this build +/// computes for that same extrinsic moves with it. Length and dispatch weight +/// are unchanged; re-measure against a live endpoint once Planck runs 147. final BigInt _liveDispatchWeight = BigInt.from(5551728000); -final BigInt _livePartialFee = BigInt.from(12962885000); +final BigInt _expectedPartialFee = BigInt.from(13622025000); final BigInt _tenQuan = BigInt.from(10).pow(13); void main() { test('inclusion fee reproduces the chain fee from length and dispatch weight', () { - expect(system_pallet.Constants().blockWeights.perClass.normal.baseExtrinsic.refTime, BigInt.from(108157000)); - expect(inclusionFee(length: 7303, dispatchWeight: _liveDispatchWeight), _livePartialFee); + expect(system_pallet.Constants().blockWeights.perClass.normal.baseExtrinsic.refTime, BigInt.from(767297000)); + expect(inclusionFee(length: 7303, dispatchWeight: _liveDispatchWeight), _expectedPartialFee); }); test('signed extrinsic length sizes the nonce at its 4-byte maximum', () { @@ -29,6 +34,6 @@ void main() { test('transfer fee is the chain fee plus the nonce headroom', () { final fee = BalancesService().transferFee(_tenQuan, dispatchWeight: _liveDispatchWeight); - expect(fee, _livePartialFee + lengthFeePerByte * BigInt.from(3)); + expect(fee, _expectedPartialFee + lengthFeePerByte * BigInt.from(3)); }); }