From 0d74daca9cb3c6f557e596033da51734b3e8564f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:02:29 +0000 Subject: [PATCH 01/16] sdk: NEAR transaction decode, hash and ML-DSA-65 signing in Rust Adds api/near.rs: a borsh TransactionV0 decoder exposed to Dart as a flat NearTransaction/NearAction model, SHA-256 transaction hashing, pure FIPS 204 ML-DSA-65 signing with an empty context (what the NEAR runtime verifies), signature verification, and the ml-dsa-65:/ml-dsa-65-hash: text forms of a key. Regenerated flutter_rust_bridge bindings; U8Array32 moves to the shared lib.dart because two modules now use it. KeypairExtensions gains signNear with hedged entropy. --- quantus_sdk/lib/quantus_sdk.dart | 3 + .../src/extensions/keypair_extensions.dart | 11 +- quantus_sdk/lib/src/rust/api/airdrop.dart | 25 +- quantus_sdk/lib/src/rust/api/crypto.dart | 64 +- quantus_sdk/lib/src/rust/api/multisig.dart | 11 +- quantus_sdk/lib/src/rust/api/near.dart | 218 +++ quantus_sdk/lib/src/rust/api/ur.dart | 11 +- quantus_sdk/lib/src/rust/api/wormhole.dart | 63 +- quantus_sdk/lib/src/rust/frb_generated.dart | 1272 ++++++++++++++--- .../lib/src/rust/frb_generated.io.dart | 187 ++- .../lib/src/rust/frb_generated.web.dart | 188 ++- quantus_sdk/lib/src/rust/lib.dart | 20 + quantus_sdk/rust/Cargo.lock | 33 + quantus_sdk/rust/Cargo.toml | 8 +- quantus_sdk/rust/src/api/mod.rs | 1 + quantus_sdk/rust/src/api/near.rs | 685 +++++++++ quantus_sdk/rust/src/frb_generated.rs | 605 +++++++- 17 files changed, 3071 insertions(+), 334 deletions(-) create mode 100644 quantus_sdk/lib/src/rust/api/near.dart create mode 100644 quantus_sdk/lib/src/rust/lib.dart create mode 100644 quantus_sdk/rust/src/api/near.rs diff --git a/quantus_sdk/lib/quantus_sdk.dart b/quantus_sdk/lib/quantus_sdk.dart index 2765e314c..75dce79f0 100644 --- a/quantus_sdk/lib/quantus_sdk.dart +++ b/quantus_sdk/lib/quantus_sdk.dart @@ -33,6 +33,7 @@ export 'src/models/event_type.dart'; export 'src/models/extrinsic_data.dart'; export 'src/models/extrinsic_fee_data.dart'; export 'src/models/signing_request.dart'; +export 'src/models/near_public_key_export.dart'; export 'src/models/unsigned_transaction_data.dart'; export 'src/models/remote_config_model.dart'; export 'src/models/miner_reward_event.dart'; @@ -58,6 +59,8 @@ export 'src/rust/api/ur.dart'; export 'src/utils/ur_qr.dart'; export 'src/rust/api/airdrop.dart'; export 'src/rust/api/wormhole.dart'; +export 'src/rust/api/near.dart'; +export 'src/rust/lib.dart' show U8Array32; export 'src/services/account_discovery_service.dart'; export 'src/services/airdrop_claim_service.dart'; export 'src/services/accounts_service.dart'; diff --git a/quantus_sdk/lib/src/extensions/keypair_extensions.dart b/quantus_sdk/lib/src/extensions/keypair_extensions.dart index 235ad75db..6604f7eac 100644 --- a/quantus_sdk/lib/src/extensions/keypair_extensions.dart +++ b/quantus_sdk/lib/src/extensions/keypair_extensions.dart @@ -1,6 +1,8 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:quantus_sdk/src/rust/api/crypto.dart' as crypto; +import 'package:quantus_sdk/src/rust/api/near.dart' as near; +import 'package:quantus_sdk/src/rust/lib.dart' show U8Array32; import 'package:ss58/ss58.dart'; extension KeypairExtensions on crypto.Keypair { @@ -10,9 +12,14 @@ extension KeypairExtensions on crypto.Keypair { /// Hedged ML-DSA signature. [specVersion] 148+ uses QUANTUS_EXTRINSIC; earlier specs use none. Uint8List sign(List message, {required int specVersion}) => crypto.signMessage(keypair: this, message: message, entropy: _hedgeEntropy(), specVersion: specVersion); + + /// Hedged pure ML-DSA-65 signature over a borsh NEAR `TransactionV0`. + /// Returns `signature ‖ public_key`, the cold-signing response NEAR tooling expects. + Uint8List signNear(List transaction) => + near.signNearTransaction(keypair: this, transaction: transaction, entropy: _hedgeEntropy()); } -crypto.U8Array32 _hedgeEntropy() { +U8Array32 _hedgeEntropy() { final random = Random.secure(); - return crypto.U8Array32(Uint8List.fromList(List.generate(32, (_) => random.nextInt(256)))); + return U8Array32(Uint8List.fromList(List.generate(32, (_) => random.nextInt(256)))); } diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index 187942698..d6485eeab 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -56,22 +56,25 @@ Future buildAirdropDilithiumClaimFromMnemonic({ required String dilithiumKeygen, required String address, required String claimAccount, -}) => RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic( - mnemonic: mnemonic, - dilithiumKeygen: dilithiumKeygen, - address: address, - claimAccount: claimAccount, -); +}) => + RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic( + mnemonic: mnemonic, + dilithiumKeygen: dilithiumKeygen, + address: address, + claimAccount: claimAccount, + ); /// Prove ownership of the wormhole address derived from `wormhole_secret` /// (current rate-8 scheme), binding the payout to `claim_account`. Builds the /// circuit in-process: expect tens of seconds of CPU on first call. Submit as /// `{"kind": "wormhole", ...fields}`. -Future proveAirdropWormhole({required List wormholeSecret, required String claimAccount}) => - RustLib.instance.api.crateApiAirdropProveAirdropWormhole( - wormholeSecret: wormholeSecret, - claimAccount: claimAccount, - ); +Future proveAirdropWormhole({ + required List wormholeSecret, + required String claimAccount, +}) => RustLib.instance.api.crateApiAirdropProveAirdropWormhole( + wormholeSecret: wormholeSecret, + claimAccount: claimAccount, +); /// A snapshot address this wallet can prove ownership of. class AirdropMatch { diff --git a/quantus_sdk/lib/src/rust/api/crypto.dart b/quantus_sdk/lib/src/rust/api/crypto.dart index 502c92c0f..ab13d7b3c 100644 --- a/quantus_sdk/lib/src/rust/api/crypto.dart +++ b/quantus_sdk/lib/src/rust/api/crypto.dart @@ -4,7 +4,7 @@ // ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import import '../frb_generated.dart'; -import 'package:collection/collection.dart'; +import '../lib.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // These functions are ignored because they are not marked as `pub`: `ml_dsa_87_from_entropy`, `new` @@ -14,28 +14,44 @@ void setDefaultSs58Prefix({required int prefix}) => RustLib.instance.api.crateApiCryptoSetDefaultSs58Prefix(prefix: prefix); /// Convert public key to accountId32 in ss58check format -String toAccountId({required Keypair obj}) => RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); +String toAccountId({required Keypair obj}) => + RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); /// Convert key in ss58check format to accountId32 -Uint8List ss58ToAccountId({required String s}) => RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); +Uint8List ss58ToAccountId({required String s}) => + RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); /// Legacy non-HD ML-DSA-87 keypair straight from the mnemonic seed (early CLI and miner accounts). -Keypair generateKeypair({required String mnemonicStr}) => - RustLib.instance.api.crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); - -Keypair generateDerivedKeypair({required String mnemonicStr, required String path, required DilithiumScheme scheme}) => - RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair(mnemonicStr: mnemonicStr, path: path, scheme: scheme); +Keypair generateKeypair({required String mnemonicStr}) => RustLib.instance.api + .crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); + +Keypair generateDerivedKeypair({ + required String mnemonicStr, + required String path, + required DilithiumScheme scheme, +}) => RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair( + mnemonicStr: mnemonicStr, + path: path, + scheme: scheme, +); -WormholeResult deriveWormhole({required String mnemonicStr, required String path}) => - RustLib.instance.api.crateApiCryptoDeriveWormhole(mnemonicStr: mnemonicStr, path: path); +WormholeResult deriveWormhole({ + required String mnemonicStr, + required String path, +}) => RustLib.instance.api.crateApiCryptoDeriveWormhole( + mnemonicStr: mnemonicStr, + path: path, +); /// Convert a first_hash (rewards preimage) to its corresponding wormhole address. /// /// Mirrors how the chain and ZK circuit derive the address from the preimage: /// - Convert 32 bytes → 4 Poseidon field elements (8 bytes each) /// - Hash once without padding -String firstHashToAddress({required String firstHashHex}) => - RustLib.instance.api.crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); +String firstHashToAddress({required String firstHashHex}) => RustLib + .instance + .api + .crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); /// ML-DSA-87 keypair from a raw 32-byte seed (dev accounts). Keypair generateKeypairFromSeed({required List seed}) => @@ -106,7 +122,11 @@ class Keypair { final Uint8List secretKey; final DilithiumScheme scheme; - const Keypair({required this.publicKey, required this.secretKey, required this.scheme}); + const Keypair({ + required this.publicKey, + required this.secretKey, + required this.scheme, + }); @override int get hashCode => publicKey.hashCode ^ secretKey.hashCode ^ scheme.hashCode; @@ -121,24 +141,16 @@ class Keypair { scheme == other.scheme; } -class U8Array32 extends NonGrowableListView { - static const arraySize = 32; - - @internal - Uint8List get inner => _inner; - final Uint8List _inner; - - U8Array32(this._inner) : assert(_inner.length == arraySize), super(_inner); - - U8Array32.init() : this(Uint8List(arraySize)); -} - class WormholeResult { final String address; final Uint8List firstHash; final Uint8List secret; - const WormholeResult({required this.address, required this.firstHash, required this.secret}); + const WormholeResult({ + required this.address, + required this.firstHash, + required this.secret, + }); @override int get hashCode => address.hashCode ^ firstHash.hashCode ^ secret.hashCode; diff --git a/quantus_sdk/lib/src/rust/api/multisig.dart b/quantus_sdk/lib/src/rust/api/multisig.dart index 8fcb105f9..cfec97749 100644 --- a/quantus_sdk/lib/src/rust/api/multisig.dart +++ b/quantus_sdk/lib/src/rust/api/multisig.dart @@ -11,5 +11,12 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Predicts the on-chain multisig address for the given signers, threshold, and nonce. /// /// Address is computed as: hash(pallet_id || sorted_signers || threshold || nonce) -String predictMultisigAddress({required List signers, required int threshold, required BigInt nonce}) => - RustLib.instance.api.crateApiMultisigPredictMultisigAddress(signers: signers, threshold: threshold, nonce: nonce); +String predictMultisigAddress({ + required List signers, + required int threshold, + required BigInt nonce, +}) => RustLib.instance.api.crateApiMultisigPredictMultisigAddress( + signers: signers, + threshold: threshold, + nonce: nonce, +); diff --git a/quantus_sdk/lib/src/rust/api/near.dart b/quantus_sdk/lib/src/rust/api/near.dart new file mode 100644 index 000000000..6224dc0ca --- /dev/null +++ b/quantus_sdk/lib/src/rust/api/near.dart @@ -0,0 +1,218 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import '../frb_generated.dart'; +import '../lib.dart'; +import 'crypto.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +// These functions are ignored because they are not marked as `pub`: `bytes`, `decode_wire`, `require_ml_dsa_65`, `text` +// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `WireAccessKeyPermission`, `WireAccessKey`, `WireAction`, `WireFunctionCallPermission`, `WirePublicKey`, `WireTransaction` +// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_variant`, `deserialize_variant`, `deserialize_variant`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `serialize`, `serialize`, `serialize`, `serialize`, `serialize`, `serialize` + +/// Decode a borsh `TransactionV0`. Refuses trailing bytes. +NearTransaction decodeNearTransaction({required List transaction}) => + RustLib.instance.api.crateApiNearDecodeNearTransaction( + transaction: transaction, + ); + +/// SHA-256 of the transaction bytes: the message NEAR signs and verifies. +Uint8List nearTransactionHash({required List transaction}) => RustLib + .instance + .api + .crateApiNearNearTransactionHash(transaction: transaction); + +/// Sign a borsh `TransactionV0` for NEAR. Refuses unless `keypair` is +/// ML-DSA-65 *and* is the key the transaction declares. Returns +/// `signature ‖ public_key` (3309 + 1952 bytes). +Uint8List signNearTransaction({ + required Keypair keypair, + required List transaction, + U8Array32? entropy, +}) => RustLib.instance.api.crateApiNearSignNearTransaction( + keypair: keypair, + transaction: transaction, + entropy: entropy, +); + +/// Whether `signature` is a valid pure ML-DSA-65 signature by `public_key` +/// over the transaction, as the NEAR runtime checks it. +bool verifyNearSignature({ + required List publicKey, + required List transaction, + required List signature, +}) => RustLib.instance.api.crateApiNearVerifyNearSignature( + publicKey: publicKey, + transaction: transaction, + signature: signature, +); + +/// The account's key in NEAR text form, `ml-dsa-65:`. This is the +/// `--signer-public-key` near-cli-rs takes and what an `AddKey` carries. +String nearPublicKeyText({required Keypair keypair}) => + RustLib.instance.api.crateApiNearNearPublicKeyText(keypair: keypair); + +/// The on-chain handle of the key, `ml-dsa-65-hash:`: what +/// `view_access_key_list` shows, SHA3-256 of a domain tag and the key. +String nearPublicKeyHandle({required Keypair keypair}) => + RustLib.instance.api.crateApiNearNearPublicKeyHandle(keypair: keypair); + +/// One action of a NEAR transaction, flattened for display. Which fields are +/// set depends on [`NearAction::kind`]; amounts are yoctoNEAR (24 decimals) +/// and keys are NEAR text form (`:`). +/// +/// A flat struct rather than an enum with payloads: that keeps the Dart side +/// a plain class instead of a generated sealed hierarchy. +class NearAction { + final NearActionKind kind; + + /// `FunctionCall`: the method; `DeleteAccount`: the beneficiary; + /// `AddKey` (function-call key): the contract the key may call. + final String? target; + + /// `FunctionCall`: raw call arguments (usually JSON). + final Uint8List? args; + + /// `FunctionCall`: prepaid gas. + final BigInt? gas; + + /// `Transfer`/`FunctionCall`: attached deposit; `Stake`: staked amount; + /// `AddKey` (function-call key): gas allowance, `None` = unlimited. + final BigInt? amount; + + /// `Stake`/`AddKey`/`DeleteKey`: the key concerned. + final String? publicKey; + + /// `AddKey`: whether the key gets full access. + final bool fullAccess; + + /// `AddKey` (function-call key): the methods it may call; empty = any. + final List methodNames; + + /// `DeployContract`: size of the code blob. + final int? codeLen; + + const NearAction({ + required this.kind, + this.target, + this.args, + this.gas, + this.amount, + this.publicKey, + required this.fullAccess, + required this.methodNames, + this.codeLen, + }); + + static Future default_() => + RustLib.instance.api.crateApiNearNearActionDefault(); + + @override + int get hashCode => + kind.hashCode ^ + target.hashCode ^ + args.hashCode ^ + gas.hashCode ^ + amount.hashCode ^ + publicKey.hashCode ^ + fullAccess.hashCode ^ + methodNames.hashCode ^ + codeLen.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NearAction && + runtimeType == other.runtimeType && + kind == other.kind && + target == other.target && + args == other.args && + gas == other.gas && + amount == other.amount && + publicKey == other.publicKey && + fullAccess == other.fullAccess && + methodNames == other.methodNames && + codeLen == other.codeLen; +} + +/// The kind of a [`NearAction`]. Everything but `Transfer` and +/// `FunctionCall` changes who controls the account or what code it runs. +enum NearActionKind { + createAccount, + deployContract, + functionCall, + transfer, + stake, + addKey, + deleteKey, + deleteAccount; + + static Future default_() => + RustLib.instance.api.crateApiNearNearActionKindDefault(); +} + +/// A decoded NEAR transaction, ready to display. +class NearTransaction { + final String signerId; + + /// The key that must sign, in NEAR text form. + final String publicKey; + + /// Raw bytes of that key; compare against a [`Keypair::public_key`]. + final Uint8List publicKeyBytes; + + /// Whether the signing key is ML-DSA-65 — the only scheme a Quantus + /// account can sign for. + final bool signsWithMlDsa65; + final BigInt nonce; + final String receiverId; + + /// Base58, as NEAR tooling prints it. + final String blockHash; + + /// SHA-256 of the transaction bytes: what gets signed, and the id the + /// NEAR RPC reports (as base58) once submitted. + final Uint8List hash; + final List actions; + + const NearTransaction({ + required this.signerId, + required this.publicKey, + required this.publicKeyBytes, + required this.signsWithMlDsa65, + required this.nonce, + required this.receiverId, + required this.blockHash, + required this.hash, + required this.actions, + }); + + @override + int get hashCode => + signerId.hashCode ^ + publicKey.hashCode ^ + publicKeyBytes.hashCode ^ + signsWithMlDsa65.hashCode ^ + nonce.hashCode ^ + receiverId.hashCode ^ + blockHash.hashCode ^ + hash.hashCode ^ + actions.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is NearTransaction && + runtimeType == other.runtimeType && + signerId == other.signerId && + publicKey == other.publicKey && + publicKeyBytes == other.publicKeyBytes && + signsWithMlDsa65 == other.signsWithMlDsa65 && + nonce == other.nonce && + receiverId == other.receiverId && + blockHash == other.blockHash && + hash == other.hash && + actions == other.actions; +} diff --git a/quantus_sdk/lib/src/rust/api/ur.dart b/quantus_sdk/lib/src/rust/api/ur.dart index 7a82bc0f2..cb91f5267 100644 --- a/quantus_sdk/lib/src/rust/api/ur.dart +++ b/quantus_sdk/lib/src/rust/api/ur.dart @@ -12,9 +12,14 @@ int maxUrParts() => RustLib.instance.api.crateApiUrMaxUrParts(); int maxUrPartChars() => RustLib.instance.api.crateApiUrMaxUrPartChars(); -Uint8List decodeUr({required List urParts}) => RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); +Uint8List decodeUr({required List urParts}) => + RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); List encodeUr({required List data, int? maxFragmentLength}) => - RustLib.instance.api.crateApiUrEncodeUr(data: data, maxFragmentLength: maxFragmentLength); + RustLib.instance.api.crateApiUrEncodeUr( + data: data, + maxFragmentLength: maxFragmentLength, + ); -bool isCompleteUr({required List urParts}) => RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); +bool isCompleteUr({required List urParts}) => + RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); diff --git a/quantus_sdk/lib/src/rust/api/wormhole.dart b/quantus_sdk/lib/src/rust/api/wormhole.dart index b94bb0597..ff954fb24 100644 --- a/quantus_sdk/lib/src/rust/api/wormhole.dart +++ b/quantus_sdk/lib/src/rust/api/wormhole.dart @@ -8,28 +8,45 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // These functions are ignored because they are not marked as `pub`: `all_required_files_exist`, `cleanup_stale_circuit_dirs`, `vec_to_32`, `vec_to_digest`, `versioned_bins_dir` -String zkCircuitsVersion() => RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); - -String computeAddressHashHex({required List rawAddress}) => - RustLib.instance.api.crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); - -Uint8List computeNullifier({required List secret, required BigInt transferCount}) => - RustLib.instance.api.crateApiWormholeComputeNullifier(secret: secret, transferCount: transferCount); +String zkCircuitsVersion() => + RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); + +String computeAddressHashHex({required List rawAddress}) => RustLib + .instance + .api + .crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); + +Uint8List computeNullifier({ + required List secret, + required BigInt transferCount, +}) => RustLib.instance.api.crateApiWormholeComputeNullifier( + secret: secret, + transferCount: transferCount, +); Uint8List computeWormholeAddress({required List secret}) => RustLib.instance.api.crateApiWormholeComputeWormholeAddress(secret: secret); -int wormholeComputeOutputAmount({required int inputAmount, required int feeBps}) => - RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount(inputAmount: inputAmount, feeBps: feeBps); +int wormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, +}) => RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount( + inputAmount: inputAmount, + feeBps: feeBps, +); int decodeLeafAmount({required List leafData}) => RustLib.instance.api.crateApiWormholeDecodeLeafAmount(leafData: leafData); -BigInt decodeLeafTransferCount({required List leafData}) => - RustLib.instance.api.crateApiWormholeDecodeLeafTransferCount(leafData: leafData); +BigInt decodeLeafTransferCount({required List leafData}) => RustLib + .instance + .api + .crateApiWormholeDecodeLeafTransferCount(leafData: leafData); -Uint8List decodeLeafToAccount({required List leafData}) => - RustLib.instance.api.crateApiWormholeDecodeLeafToAccount(leafData: leafData); +Uint8List decodeLeafToAccount({required List leafData}) => RustLib + .instance + .api + .crateApiWormholeDecodeLeafToAccount(leafData: leafData); MerkleProcessed computeMerklePositions({ required List unsortedSiblingsFlat, @@ -41,8 +58,10 @@ MerkleProcessed computeMerklePositions({ depth: depth, ); -Future ensureCircuitBinaries({required String binsDir}) => - RustLib.instance.api.crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); +Future ensureCircuitBinaries({required String binsDir}) => RustLib + .instance + .api + .crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); Future generateProof({ required ProofInput input, @@ -54,14 +73,22 @@ Future generateProof({ commonBinPath: commonBinPath, ); -Future aggregateProofs({required List proofBytesList, required String binsDir}) => - RustLib.instance.api.crateApiWormholeAggregateProofs(proofBytesList: proofBytesList, binsDir: binsDir); +Future aggregateProofs({ + required List proofBytesList, + required String binsDir, +}) => RustLib.instance.api.crateApiWormholeAggregateProofs( + proofBytesList: proofBytesList, + binsDir: binsDir, +); class MerkleProcessed { final Uint8List sortedSiblingsFlat; final Uint8List positions; - const MerkleProcessed({required this.sortedSiblingsFlat, required this.positions}); + const MerkleProcessed({ + required this.sortedSiblingsFlat, + required this.positions, + }); @override int get hashCode => sortedSiblingsFlat.hashCode ^ positions.hashCode; diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index 60c08e8dc..36115e167 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -6,12 +6,15 @@ import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; +import 'api/near.dart'; import 'api/ur.dart'; import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'frb_generated.dart'; -import 'frb_generated.io.dart' if (dart.library.js_interop) 'frb_generated.web.dart'; +import 'frb_generated.io.dart' + if (dart.library.js_interop) 'frb_generated.web.dart'; +import 'lib.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Main entrypoint of the Rust API @@ -49,10 +52,12 @@ class RustLib extends BaseEntrypoint { static void dispose() => instance.disposeImpl(); @override - ApiImplConstructor get apiImplConstructor => RustLibApiImpl.new; + ApiImplConstructor get apiImplConstructor => + RustLibApiImpl.new; @override - WireConstructor get wireConstructor => RustLibWire.fromExternalLibrary; + WireConstructor get wireConstructor => + RustLibWire.fromExternalLibrary; @override Future executeRustInitializers() async { @@ -60,24 +65,29 @@ class RustLib extends BaseEntrypoint { } @override - ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => kDefaultExternalLibraryLoaderConfig; + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => + kDefaultExternalLibraryLoaderConfig; @override String get codegenVersion => '2.12.0'; @override - int get rustContentHash => -1962506674; - - static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( - stem: 'rust_lib_quantus_wallet', - ioDirectory: 'rust/target/release/', - webPrefix: 'pkg/', - wasmBindgenName: 'wasm_bindgen', - ); + int get rustContentHash => -91816623; + + static const kDefaultExternalLibraryLoaderConfig = + ExternalLibraryLoaderConfig( + stem: 'rust_lib_quantus_wallet', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + wasmBindgenName: 'wasm_bindgen', + ); } abstract class RustLibApi extends BaseApi { - Future crateApiWormholeAggregateProofs({required List proofBytesList, required String binsDir}); + Future crateApiWormholeAggregateProofs({ + required List proofBytesList, + required String binsDir, + }); Future crateApiAirdropBuildAirdropDilithiumClaim({ required Keypair keypair, @@ -85,7 +95,8 @@ abstract class RustLibApi extends BaseApi { required String claimAccount, }); - Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + Future + crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ required String mnemonic, required String dilithiumKeygen, required String address, @@ -100,7 +111,10 @@ abstract class RustLibApi extends BaseApi { required int depth, }); - Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}); + Uint8List crateApiWormholeComputeNullifier({ + required List secret, + required BigInt transferCount, + }); Uint8List crateApiWormholeComputeWormholeAddress({required List secret}); @@ -116,13 +130,25 @@ abstract class RustLibApi extends BaseApi { BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}); + NearTransaction crateApiNearDecodeNearTransaction({ + required List transaction, + }); + Uint8List crateApiUrDecodeUr({required List urParts}); - WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}); + WormholeResult crateApiCryptoDeriveWormhole({ + required String mnemonicStr, + required String path, + }); - List crateApiUrEncodeUr({required List data, int? maxFragmentLength}); + List crateApiUrEncodeUr({ + required List data, + int? maxFragmentLength, + }); - Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}); + Future crateApiWormholeEnsureCircuitBinaries({ + required String binsDir, + }); Future> crateApiAirdropFindAirdropMatches({ required List snapshotAddresses, @@ -157,6 +183,16 @@ abstract class RustLibApi extends BaseApi { int crateApiUrMaxUrParts(); + Future crateApiNearNearActionDefault(); + + Future crateApiNearNearActionKindDefault(); + + String crateApiNearNearPublicKeyHandle({required Keypair keypair}); + + String crateApiNearNearPublicKeyText({required Keypair keypair}); + + Uint8List crateApiNearNearTransactionHash({required List transaction}); + String crateApiMultisigPredictMultisigAddress({ required List signers, required int threshold, @@ -188,6 +224,12 @@ abstract class RustLibApi extends BaseApi { required int specVersion, }); + Uint8List crateApiNearSignNearTransaction({ + required Keypair keypair, + required List transaction, + U8Array32? entropy, + }); + int crateApiCryptoSignatureBytes({required DilithiumScheme scheme}); Uint8List crateApiCryptoSs58ToAccountId({required String s}); @@ -201,15 +243,27 @@ abstract class RustLibApi extends BaseApi { required int specVersion, }); - int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}); + bool crateApiNearVerifyNearSignature({ + required List publicKey, + required List transaction, + required List signature, + }); + + int crateApiWormholeWormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, + }); String crateApiWormholeZkCircuitsVersion(); - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError; + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_HdLatticeError; - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError; + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_HdLatticeError; - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr; + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -231,9 +285,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_list_prim_u_8_strict(proofBytesList, serializer); sse_encode_String(binsDir, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 1, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeAggregateProofsConstMeta, argValues: [proofBytesList, binsDir], apiImpl: this, @@ -242,7 +304,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeAggregateProofsConstMeta => - const TaskConstMeta(debugName: 'aggregate_proofs', argNames: ['proofBytesList', 'binsDir']); + const TaskConstMeta( + debugName: 'aggregate_proofs', + argNames: ['proofBytesList', 'binsDir'], + ); @override Future crateApiAirdropBuildAirdropDilithiumClaim({ @@ -257,9 +322,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(keypair, serializer); sse_encode_String(address, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 2, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_dilithium_claim_body, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta, argValues: [keypair, address, claimAccount], apiImpl: this, @@ -268,10 +341,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta => - const TaskConstMeta(debugName: 'build_airdrop_dilithium_claim', argNames: ['keypair', 'address', 'claimAccount']); + const TaskConstMeta( + debugName: 'build_airdrop_dilithium_claim', + argNames: ['keypair', 'address', 'claimAccount'], + ); @override - Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + Future + crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ required String mnemonic, required String dilithiumKeygen, required String address, @@ -285,23 +362,36 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(dilithiumKeygen, serializer); sse_encode_String(address, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 3, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), - constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta, + codec: SseCodec( + decodeSuccessData: sse_decode_dilithium_claim_body, + decodeErrorData: sse_decode_String, + ), + constMeta: + kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta, argValues: [mnemonic, dilithiumKeygen, address, claimAccount], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta => const TaskConstMeta( - debugName: 'build_airdrop_dilithium_claim_from_mnemonic', - argNames: ['mnemonic', 'dilithiumKeygen', 'address', 'claimAccount'], - ); + TaskConstMeta + get kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta => + const TaskConstMeta( + debugName: 'build_airdrop_dilithium_claim_from_mnemonic', + argNames: ['mnemonic', 'dilithiumKeygen', 'address', 'claimAccount'], + ); @override - String crateApiWormholeComputeAddressHashHex({required List rawAddress}) { + String crateApiWormholeComputeAddressHashHex({ + required List rawAddress, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -309,7 +399,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(rawAddress, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeAddressHashHexConstMeta, argValues: [rawAddress], apiImpl: this, @@ -318,7 +411,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeAddressHashHexConstMeta => - const TaskConstMeta(debugName: 'compute_address_hash_hex', argNames: ['rawAddress']); + const TaskConstMeta( + debugName: 'compute_address_hash_hex', + argNames: ['rawAddress'], + ); @override MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -335,7 +431,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(depth, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_merkle_processed, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_merkle_processed, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeMerklePositionsConstMeta, argValues: [unsortedSiblingsFlat, leafHash, depth], apiImpl: this, @@ -343,13 +442,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => const TaskConstMeta( - debugName: 'compute_merkle_positions', - argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], - ); + TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => + const TaskConstMeta( + debugName: 'compute_merkle_positions', + argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], + ); @override - Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}) { + Uint8List crateApiWormholeComputeNullifier({ + required List secret, + required BigInt transferCount, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -358,7 +461,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(transferCount, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeNullifierConstMeta, argValues: [secret, transferCount], apiImpl: this, @@ -367,10 +473,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeNullifierConstMeta => - const TaskConstMeta(debugName: 'compute_nullifier', argNames: ['secret', 'transferCount']); + const TaskConstMeta( + debugName: 'compute_nullifier', + argNames: ['secret', 'transferCount'], + ); @override - Uint8List crateApiWormholeComputeWormholeAddress({required List secret}) { + Uint8List crateApiWormholeComputeWormholeAddress({ + required List secret, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -378,7 +489,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(secret, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeComputeWormholeAddressConstMeta, argValues: [secret], apiImpl: this, @@ -387,7 +501,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeWormholeAddressConstMeta => - const TaskConstMeta(debugName: 'compute_wormhole_address', argNames: ['secret']); + const TaskConstMeta( + debugName: 'compute_wormhole_address', + argNames: ['secret'], + ); @override Keypair crateApiCryptoCrystalAlice() { @@ -397,7 +514,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalAliceConstMeta, argValues: [], apiImpl: this, @@ -416,7 +536,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalBobConstMeta, argValues: [], apiImpl: this, @@ -424,7 +547,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => const TaskConstMeta(debugName: 'crystal_bob', argNames: []); + TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => + const TaskConstMeta(debugName: 'crystal_bob', argNames: []); @override Keypair crateApiCryptoCrystalCharlie() { @@ -434,7 +558,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoCrystalCharlieConstMeta, argValues: [], apiImpl: this, @@ -454,7 +581,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafAmountConstMeta, argValues: [leafData], apiImpl: this, @@ -463,7 +593,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafAmountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_amount', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_amount', + argNames: ['leafData'], + ); @override Uint8List crateApiWormholeDecodeLeafToAccount({required List leafData}) { @@ -474,7 +607,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafToAccountConstMeta, argValues: [leafData], apiImpl: this, @@ -483,10 +619,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafToAccountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_to_account', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_to_account', + argNames: ['leafData'], + ); @override - BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}) { + BigInt crateApiWormholeDecodeLeafTransferCount({ + required List leafData, + }) { return handler.executeSync( SyncTask( callFfi: () { @@ -494,7 +635,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_64, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_64, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeDecodeLeafTransferCountConstMeta, argValues: [leafData], apiImpl: this, @@ -503,7 +647,38 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafTransferCountConstMeta => - const TaskConstMeta(debugName: 'decode_leaf_transfer_count', argNames: ['leafData']); + const TaskConstMeta( + debugName: 'decode_leaf_transfer_count', + argNames: ['leafData'], + ); + + @override + NearTransaction crateApiNearDecodeNearTransaction({ + required List transaction, + }) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(transaction, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_near_transaction, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiNearDecodeNearTransactionConstMeta, + argValues: [transaction], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearDecodeNearTransactionConstMeta => + const TaskConstMeta( + debugName: 'decode_near_transaction', + argNames: ['transaction'], + ); @override Uint8List crateApiUrDecodeUr({required List urParts}) { @@ -512,9 +687,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiUrDecodeUrConstMeta, argValues: [urParts], apiImpl: this, @@ -522,17 +700,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrDecodeUrConstMeta => const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); + TaskConstMeta get kCrateApiUrDecodeUrConstMeta => + const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); @override - WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}) { + WormholeResult crateApiCryptoDeriveWormhole({ + required String mnemonicStr, + required String path, + }) { return handler.executeSync( SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16)!; }, codec: SseCodec( decodeSuccessData: sse_decode_wormhole_result, @@ -547,19 +729,28 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoDeriveWormholeConstMeta => - const TaskConstMeta(debugName: 'derive_wormhole', argNames: ['mnemonicStr', 'path']); + const TaskConstMeta( + debugName: 'derive_wormhole', + argNames: ['mnemonicStr', 'path'], + ); @override - List crateApiUrEncodeUr({required List data, int? maxFragmentLength}) { + List crateApiUrEncodeUr({ + required List data, + int? maxFragmentLength, + }) { return handler.executeSync( SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(data, serializer); sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 16)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiUrEncodeUrConstMeta, argValues: [data, maxFragmentLength], apiImpl: this, @@ -567,19 +758,31 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrEncodeUrConstMeta => - const TaskConstMeta(debugName: 'encode_ur', argNames: ['data', 'maxFragmentLength']); + TaskConstMeta get kCrateApiUrEncodeUrConstMeta => const TaskConstMeta( + debugName: 'encode_ur', + argNames: ['data', 'maxFragmentLength'], + ); @override - Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}) { + Future crateApiWormholeEnsureCircuitBinaries({ + required String binsDir, + }) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(binsDir, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 18, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeEnsureCircuitBinariesConstMeta, argValues: [binsDir], apiImpl: this, @@ -588,7 +791,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeEnsureCircuitBinariesConstMeta => - const TaskConstMeta(debugName: 'ensure_circuit_binaries', argNames: ['binsDir']); + const TaskConstMeta( + debugName: 'ensure_circuit_binaries', + argNames: ['binsDir'], + ); @override Future> crateApiAirdropFindAirdropMatches({ @@ -604,21 +810,43 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(snapshotAddresses, serializer); sse_encode_opt_list_prim_u_8_strict(dilithiumPublicKey, serializer); sse_encode_opt_String(mnemonic, serializer); - sse_encode_list_list_prim_u_8_strict(extraWormholeSecrets, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18, port: port_); + sse_encode_list_list_prim_u_8_strict( + extraWormholeSecrets, + serializer, + ); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 19, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_list_airdrop_match, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_airdrop_match, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiAirdropFindAirdropMatchesConstMeta, - argValues: [snapshotAddresses, dilithiumPublicKey, mnemonic, extraWormholeSecrets], + argValues: [ + snapshotAddresses, + dilithiumPublicKey, + mnemonic, + extraWormholeSecrets, + ], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => const TaskConstMeta( - debugName: 'find_airdrop_matches', - argNames: ['snapshotAddresses', 'dilithiumPublicKey', 'mnemonic', 'extraWormholeSecrets'], - ); + TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => + const TaskConstMeta( + debugName: 'find_airdrop_matches', + argNames: [ + 'snapshotAddresses', + 'dilithiumPublicKey', + 'mnemonic', + 'extraWormholeSecrets', + ], + ); @override String crateApiCryptoFirstHashToAddress({required String firstHashHex}) { @@ -627,9 +855,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(firstHashHex, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiCryptoFirstHashToAddressConstMeta, argValues: [firstHashHex], apiImpl: this, @@ -638,7 +869,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoFirstHashToAddressConstMeta => - const TaskConstMeta(debugName: 'first_hash_to_address', argNames: ['firstHashHex']); + const TaskConstMeta( + debugName: 'first_hash_to_address', + argNames: ['firstHashHex'], + ); @override Keypair crateApiCryptoGenerateDerivedKeypair({ @@ -653,7 +887,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(mnemonicStr, serializer); sse_encode_String(path, serializer); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -668,7 +902,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateDerivedKeypairConstMeta => - const TaskConstMeta(debugName: 'generate_derived_keypair', argNames: ['mnemonicStr', 'path', 'scheme']); + const TaskConstMeta( + debugName: 'generate_derived_keypair', + argNames: ['mnemonicStr', 'path', 'scheme'], + ); @override Keypair crateApiCryptoGenerateKeypair({required String mnemonicStr}) { @@ -677,7 +914,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(mnemonicStr, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 21)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22)!; }, codec: SseCodec( decodeSuccessData: sse_decode_keypair, @@ -692,7 +929,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairConstMeta => - const TaskConstMeta(debugName: 'generate_keypair', argNames: ['mnemonicStr']); + const TaskConstMeta( + debugName: 'generate_keypair', + argNames: ['mnemonicStr'], + ); @override Keypair crateApiCryptoGenerateKeypairFromSeed({required List seed}) { @@ -701,9 +941,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(seed, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 22)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_keypair, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoGenerateKeypairFromSeedConstMeta, argValues: [seed], apiImpl: this, @@ -712,7 +955,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairFromSeedConstMeta => - const TaskConstMeta(debugName: 'generate_keypair_from_seed', argNames: ['seed']); + const TaskConstMeta( + debugName: 'generate_keypair_from_seed', + argNames: ['seed'], + ); @override Future crateApiWormholeGenerateProof({ @@ -727,9 +973,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_proof_input(input, serializer); sse_encode_String(proverBinPath, serializer); sse_encode_String(commonBinPath, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 24, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_proof_output, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_proof_output, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeGenerateProofConstMeta, argValues: [input, proverBinPath, commonBinPath], apiImpl: this, @@ -738,7 +992,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeGenerateProofConstMeta => - const TaskConstMeta(debugName: 'generate_proof', argNames: ['input', 'proverBinPath', 'commonBinPath']); + const TaskConstMeta( + debugName: 'generate_proof', + argNames: ['input', 'proverBinPath', 'commonBinPath'], + ); @override Future crateApiCryptoInitApp() { @@ -746,9 +1003,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 25, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoInitAppConstMeta, argValues: [], apiImpl: this, @@ -756,7 +1021,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoInitAppConstMeta => const TaskConstMeta(debugName: 'init_app', argNames: []); + TaskConstMeta get kCrateApiCryptoInitAppConstMeta => + const TaskConstMeta(debugName: 'init_app', argNames: []); @override bool crateApiUrIsCompleteUr({required List urParts}) { @@ -765,9 +1031,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_String(urParts, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), constMeta: kCrateApiUrIsCompleteUrConstMeta, argValues: [urParts], apiImpl: this, @@ -784,9 +1053,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiUrMaxUrPartCharsConstMeta, argValues: [], apiImpl: this, @@ -803,9 +1075,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiUrMaxUrPartsConstMeta, argValues: [], apiImpl: this, @@ -813,7 +1088,140 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); + TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => + const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); + + @override + Future crateApiNearNearActionDefault() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 29, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_near_action, + decodeErrorData: null, + ), + constMeta: kCrateApiNearNearActionDefaultConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearNearActionDefaultConstMeta => + const TaskConstMeta(debugName: 'near_action_default', argNames: []); + + @override + Future crateApiNearNearActionKindDefault() { + return handler.executeNormal( + NormalTask( + callFfi: (port_) { + final serializer = SseSerializer(generalizedFrbRustBinding); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 30, + port: port_, + ); + }, + codec: SseCodec( + decodeSuccessData: sse_decode_near_action_kind, + decodeErrorData: null, + ), + constMeta: kCrateApiNearNearActionKindDefaultConstMeta, + argValues: [], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearNearActionKindDefaultConstMeta => + const TaskConstMeta(debugName: 'near_action_kind_default', argNames: []); + + @override + String crateApiNearNearPublicKeyHandle({required Keypair keypair}) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_keypair(keypair, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiNearNearPublicKeyHandleConstMeta, + argValues: [keypair], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearNearPublicKeyHandleConstMeta => + const TaskConstMeta( + debugName: 'near_public_key_handle', + argNames: ['keypair'], + ); + + @override + String crateApiNearNearPublicKeyText({required Keypair keypair}) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_keypair(keypair, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiNearNearPublicKeyTextConstMeta, + argValues: [keypair], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearNearPublicKeyTextConstMeta => + const TaskConstMeta( + debugName: 'near_public_key_text', + argNames: ['keypair'], + ); + + @override + Uint8List crateApiNearNearTransactionHash({required List transaction}) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(transaction, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: null, + ), + constMeta: kCrateApiNearNearTransactionHashConstMeta, + argValues: [transaction], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearNearTransactionHashConstMeta => + const TaskConstMeta( + debugName: 'near_transaction_hash', + argNames: ['transaction'], + ); @override String crateApiMultisigPredictMultisigAddress({ @@ -828,9 +1236,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(signers, serializer); sse_encode_u_32(threshold, serializer); sse_encode_u_64(nonce, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiMultisigPredictMultisigAddressConstMeta, argValues: [signers, threshold, nonce], apiImpl: this, @@ -839,7 +1250,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiMultisigPredictMultisigAddressConstMeta => - const TaskConstMeta(debugName: 'predict_multisig_address', argNames: ['signers', 'threshold', 'nonce']); + const TaskConstMeta( + debugName: 'predict_multisig_address', + argNames: ['signers', 'threshold', 'nonce'], + ); @override Future crateApiAirdropProveAirdropWormhole({ @@ -852,9 +1266,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(wormholeSecret, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29, port: port_); + pdeCallFfi( + generalizedFrbRustBinding, + serializer, + funcId: 35, + port: port_, + ); }, - codec: SseCodec(decodeSuccessData: sse_decode_wormhole_claim_body, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_wormhole_claim_body, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiAirdropProveAirdropWormholeConstMeta, argValues: [wormholeSecret, claimAccount], apiImpl: this, @@ -863,7 +1285,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropProveAirdropWormholeConstMeta => - const TaskConstMeta(debugName: 'prove_airdrop_wormhole', argNames: ['wormholeSecret', 'claimAccount']); + const TaskConstMeta( + debugName: 'prove_airdrop_wormhole', + argNames: ['wormholeSecret', 'claimAccount'], + ); @override int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}) { @@ -872,9 +1297,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoPublicKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -892,9 +1320,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSecretKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -912,9 +1343,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_16(prefix, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_unit, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSetDefaultSs58PrefixConstMeta, argValues: [prefix], apiImpl: this, @@ -923,7 +1357,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoSetDefaultSs58PrefixConstMeta => - const TaskConstMeta(debugName: 'set_default_ss58_prefix', argNames: ['prefix']); + const TaskConstMeta( + debugName: 'set_default_ss58_prefix', + argNames: ['prefix'], + ); @override Uint8List crateApiCryptoSignMessage({ @@ -940,9 +1377,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignMessageConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -950,8 +1390,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => - const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy', 'specVersion']); + TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => const TaskConstMeta( + debugName: 'sign_message', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); @override Uint8List crateApiCryptoSignMessageWithPubkey({ @@ -968,9 +1410,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_opt_u_8_array_32(entropy, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -978,10 +1423,43 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => const TaskConstMeta( - debugName: 'sign_message_with_pubkey', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => + const TaskConstMeta( + debugName: 'sign_message_with_pubkey', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); + + @override + Uint8List crateApiNearSignNearTransaction({ + required Keypair keypair, + required List transaction, + U8Array32? entropy, + }) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_box_autoadd_keypair(keypair, serializer); + sse_encode_list_prim_u_8_loose(transaction, serializer); + sse_encode_opt_u_8_array_32(entropy, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 41)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), + constMeta: kCrateApiNearSignNearTransactionConstMeta, + argValues: [keypair, transaction, entropy], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearSignNearTransactionConstMeta => + const TaskConstMeta( + debugName: 'sign_near_transaction', + argNames: ['keypair', 'transaction', 'entropy'], + ); @override int crateApiCryptoSignatureBytes({required DilithiumScheme scheme}) { @@ -990,9 +1468,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_dilithium_scheme(scheme, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 42)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoSignatureBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1010,9 +1491,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(s, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 43)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_list_prim_u_8_strict, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiCryptoSs58ToAccountIdConstMeta, argValues: [s], apiImpl: this, @@ -1030,9 +1514,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_box_autoadd_keypair(obj, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 44)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoToAccountIdConstMeta, argValues: [obj], apiImpl: this, @@ -1058,9 +1545,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(message, serializer); sse_encode_list_prim_u_8_loose(signature, serializer); sse_encode_u_32(specVersion, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 45)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), constMeta: kCrateApiCryptoVerifyMessageConstMeta, argValues: [keypair, message, signature, specVersion], apiImpl: this, @@ -1069,19 +1559,60 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoVerifyMessageConstMeta => - const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature', 'specVersion']); + const TaskConstMeta( + debugName: 'verify_message', + argNames: ['keypair', 'message', 'signature', 'specVersion'], + ); @override - int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}) { + bool crateApiNearVerifyNearSignature({ + required List publicKey, + required List transaction, + required List signature, + }) { + return handler.executeSync( + SyncTask( + callFfi: () { + final serializer = SseSerializer(generalizedFrbRustBinding); + sse_encode_list_prim_u_8_loose(publicKey, serializer); + sse_encode_list_prim_u_8_loose(transaction, serializer); + sse_encode_list_prim_u_8_loose(signature, serializer); + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 46)!; + }, + codec: SseCodec( + decodeSuccessData: sse_decode_bool, + decodeErrorData: null, + ), + constMeta: kCrateApiNearVerifyNearSignatureConstMeta, + argValues: [publicKey, transaction, signature], + apiImpl: this, + ), + ); + } + + TaskConstMeta get kCrateApiNearVerifyNearSignatureConstMeta => + const TaskConstMeta( + debugName: 'verify_near_signature', + argNames: ['publicKey', 'transaction', 'signature'], + ); + + @override + int crateApiWormholeWormholeComputeOutputAmount({ + required int inputAmount, + required int feeBps, + }) { return handler.executeSync( SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_u_32(inputAmount, serializer); sse_encode_u_32(feeBps, serializer); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 47)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), + codec: SseCodec( + decodeSuccessData: sse_decode_u_32, + decodeErrorData: sse_decode_String, + ), constMeta: kCrateApiWormholeWormholeComputeOutputAmountConstMeta, argValues: [inputAmount, feeBps], apiImpl: this, @@ -1090,7 +1621,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeWormholeComputeOutputAmountConstMeta => - const TaskConstMeta(debugName: 'wormhole_compute_output_amount', argNames: ['inputAmount', 'feeBps']); + const TaskConstMeta( + debugName: 'wormhole_compute_output_amount', + argNames: ['inputAmount', 'feeBps'], + ); @override String crateApiWormholeZkCircuitsVersion() { @@ -1098,9 +1632,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { SyncTask( callFfi: () { final serializer = SseSerializer(generalizedFrbRustBinding); - return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40)!; + return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 48)!; }, - codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), + codec: SseCodec( + decodeSuccessData: sse_decode_String, + decodeErrorData: null, + ), constMeta: kCrateApiWormholeZkCircuitsVersionConstMeta, argValues: [], apiImpl: this, @@ -1111,14 +1648,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiWormholeZkCircuitsVersionConstMeta => const TaskConstMeta(debugName: 'zk_circuits_version', argNames: []); - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError => - wire.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcIncrementStrongCountFnType + get rust_arc_increment_strong_count_HdLatticeError => wire + .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError => - wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcDecrementStrongCountFnType + get rust_arc_decrement_strong_count_HdLatticeError => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1126,7 +1666,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw) { + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ) { // Codec=Dco (DartCObject based), see doc to use other codecs return HdLatticeErrorImpl.frbInternalDcoDecode(raw as List); } @@ -1137,11 +1680,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as String; } + @protected + BigInt dco_decode_U128(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return BigInt.parse(raw); + } + @protected AirdropMatch dco_decode_airdrop_match(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); + if (arr.length != 7) + throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); return AirdropMatch( address: dco_decode_String(arr[0]), kind: dco_decode_String(arr[1]), @@ -1177,11 +1727,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw as int; } + @protected + BigInt dco_decode_box_autoadd_u_64(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return dco_decode_u_64(raw); + } + @protected DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 6) + throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); return DilithiumClaimBody( scheme: dco_decode_String(arr[0]), address: dco_decode_String(arr[1]), @@ -1214,7 +1771,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Keypair dco_decode_keypair(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) + throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return Keypair( publicKey: dco_decode_list_prim_u_8_strict(arr[0]), secretKey: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1240,6 +1798,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return (raw as List).map(dco_decode_list_prim_u_8_strict).toList(); } + @protected + List dco_decode_list_near_action(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return (raw as List).map(dco_decode_near_action).toList(); + } + @protected List dco_decode_list_prim_u_8_loose(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1256,25 +1820,82 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { MerkleProcessed dco_decode_merkle_processed(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return MerkleProcessed( sortedSiblingsFlat: dco_decode_list_prim_u_8_strict(arr[0]), positions: dco_decode_list_prim_u_8_strict(arr[1]), ); } + @protected + NearAction dco_decode_near_action(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 9) + throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); + return NearAction( + kind: dco_decode_near_action_kind(arr[0]), + target: dco_decode_opt_String(arr[1]), + args: dco_decode_opt_list_prim_u_8_strict(arr[2]), + gas: dco_decode_opt_box_autoadd_u_64(arr[3]), + amount: dco_decode_opt_U128(arr[4]), + publicKey: dco_decode_opt_String(arr[5]), + fullAccess: dco_decode_bool(arr[6]), + methodNames: dco_decode_list_String(arr[7]), + codeLen: dco_decode_opt_box_autoadd_u_32(arr[8]), + ); + } + + @protected + NearActionKind dco_decode_near_action_kind(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return NearActionKind.values[raw as int]; + } + + @protected + NearTransaction dco_decode_near_transaction(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 9) + throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); + return NearTransaction( + signerId: dco_decode_String(arr[0]), + publicKey: dco_decode_String(arr[1]), + publicKeyBytes: dco_decode_list_prim_u_8_strict(arr[2]), + signsWithMlDsa65: dco_decode_bool(arr[3]), + nonce: dco_decode_u_64(arr[4]), + receiverId: dco_decode_String(arr[5]), + blockHash: dco_decode_String(arr[6]), + hash: dco_decode_list_prim_u_8_strict(arr[7]), + actions: dco_decode_list_near_action(arr[8]), + ); + } + @protected String? dco_decode_opt_String(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return raw == null ? null : dco_decode_String(raw); } + @protected + BigInt? dco_decode_opt_U128(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_U128(raw); + } + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return raw == null ? null : dco_decode_box_autoadd_u_32(raw); } + @protected + BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return raw == null ? null : dco_decode_box_autoadd_u_64(raw); + } + @protected Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1291,7 +1912,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofInput dco_decode_proof_input(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 19) throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); + if (arr.length != 19) + throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); return ProofInput( secret: dco_decode_list_prim_u_8_strict(arr[0]), transferCount: dco_decode_u_64(arr[1]), @@ -1319,7 +1941,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofOutput dco_decode_proof_output(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return ProofOutput( proofBytes: dco_decode_list_prim_u_8_strict(arr[0]), nullifier: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1372,15 +1995,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); - return WormholeClaimBody(proofKind: dco_decode_String(arr[0]), proofHex: dco_decode_String(arr[1])); + if (arr.length != 2) + throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return WormholeClaimBody( + proofKind: dco_decode_String(arr[0]), + proofHex: dco_decode_String(arr[1]), + ); } @protected WormholeResult dco_decode_wormhole_result(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) + throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return WormholeResult( address: dco_decode_String(arr[0]), firstHash: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1389,19 +2017,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); + return HdLatticeErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); } @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); + return HdLatticeErrorImpl.frbInternalSseDecode( + sse_decode_usize(deserializer), + sse_decode_i_32(deserializer), + ); } @protected @@ -1411,6 +2047,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return utf8.decoder.convert(inner); } + @protected + BigInt sse_decode_U128(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var inner = sse_decode_String(deserializer); + return BigInt.parse(inner); + } + @protected AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1457,7 +2100,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer) { + BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + return (sse_decode_u_64(deserializer)); + } + + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs var var_scheme = sse_decode_String(deserializer); var var_address = sse_decode_String(deserializer); @@ -1500,7 +2151,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_publicKey = sse_decode_list_prim_u_8_strict(deserializer); var var_secretKey = sse_decode_list_prim_u_8_strict(deserializer); var var_scheme = sse_decode_dilithium_scheme(deserializer); - return Keypair(publicKey: var_publicKey, secretKey: var_secretKey, scheme: var_scheme); + return Keypair( + publicKey: var_publicKey, + secretKey: var_secretKey, + scheme: var_scheme, + ); } @protected @@ -1516,7 +2171,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_airdrop_match(SseDeserializer deserializer) { + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -1528,7 +2185,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer) { + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -1539,6 +2198,18 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return ans_; } + @protected + List sse_decode_list_near_action(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + var len_ = sse_decode_i_32(deserializer); + var ans_ = []; + for (var idx_ = 0; idx_ < len_; ++idx_) { + ans_.add(sse_decode_near_action(deserializer)); + } + return ans_; + } + @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1558,7 +2229,67 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs var var_sortedSiblingsFlat = sse_decode_list_prim_u_8_strict(deserializer); var var_positions = sse_decode_list_prim_u_8_strict(deserializer); - return MerkleProcessed(sortedSiblingsFlat: var_sortedSiblingsFlat, positions: var_positions); + return MerkleProcessed( + sortedSiblingsFlat: var_sortedSiblingsFlat, + positions: var_positions, + ); + } + + @protected + NearAction sse_decode_near_action(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_kind = sse_decode_near_action_kind(deserializer); + var var_target = sse_decode_opt_String(deserializer); + var var_args = sse_decode_opt_list_prim_u_8_strict(deserializer); + var var_gas = sse_decode_opt_box_autoadd_u_64(deserializer); + var var_amount = sse_decode_opt_U128(deserializer); + var var_publicKey = sse_decode_opt_String(deserializer); + var var_fullAccess = sse_decode_bool(deserializer); + var var_methodNames = sse_decode_list_String(deserializer); + var var_codeLen = sse_decode_opt_box_autoadd_u_32(deserializer); + return NearAction( + kind: var_kind, + target: var_target, + args: var_args, + gas: var_gas, + amount: var_amount, + publicKey: var_publicKey, + fullAccess: var_fullAccess, + methodNames: var_methodNames, + codeLen: var_codeLen, + ); + } + + @protected + NearActionKind sse_decode_near_action_kind(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var inner = sse_decode_i_32(deserializer); + return NearActionKind.values[inner]; + } + + @protected + NearTransaction sse_decode_near_transaction(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_signerId = sse_decode_String(deserializer); + var var_publicKey = sse_decode_String(deserializer); + var var_publicKeyBytes = sse_decode_list_prim_u_8_strict(deserializer); + var var_signsWithMlDsa65 = sse_decode_bool(deserializer); + var var_nonce = sse_decode_u_64(deserializer); + var var_receiverId = sse_decode_String(deserializer); + var var_blockHash = sse_decode_String(deserializer); + var var_hash = sse_decode_list_prim_u_8_strict(deserializer); + var var_actions = sse_decode_list_near_action(deserializer); + return NearTransaction( + signerId: var_signerId, + publicKey: var_publicKey, + publicKeyBytes: var_publicKeyBytes, + signsWithMlDsa65: var_signsWithMlDsa65, + nonce: var_nonce, + receiverId: var_receiverId, + blockHash: var_blockHash, + hash: var_hash, + actions: var_actions, + ); } @protected @@ -1572,6 +2303,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + BigInt? sse_decode_opt_U128(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_U128(deserializer)); + } else { + return null; + } + } + @protected int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1583,6 +2325,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + if (sse_decode_bool(deserializer)) { + return (sse_decode_box_autoadd_u_64(deserializer)); + } else { + return null; + } + } + @protected Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1701,7 +2454,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer) { + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs var var_proofKind = sse_decode_String(deserializer); var var_proofHex = sse_decode_String(deserializer); @@ -1714,25 +2469,37 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_address = sse_decode_String(deserializer); var var_firstHash = sse_decode_list_prim_u_8_strict(deserializer); var var_secret = sse_decode_list_prim_u_8_strict(deserializer); - return WormholeResult(address: var_address, firstHash: var_firstHash, secret: var_secret); + return WormholeResult( + address: var_address, + firstHash: var_firstHash, + secret: var_secret, + ); } @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), serializer); + sse_encode_usize( + (self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), + serializer, + ); } @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), serializer); + sse_encode_usize( + (self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), + serializer, + ); } @protected @@ -1741,6 +2508,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_strict(utf8.encoder.convert(self), serializer); } + @protected + void sse_encode_U128(BigInt self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.toString(), serializer); + } + @protected void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1766,7 +2539,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer) { + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_proof_input(self, serializer); } @@ -1778,7 +2554,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer) { + void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_u_64(self, serializer); + } + + @protected + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.scheme, serializer); sse_encode_String(self.address, serializer); @@ -1789,7 +2574,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer) { + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.index, serializer); } @@ -1824,7 +2612,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_airdrop_match(List self, SseSerializer serializer) { + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -1833,7 +2624,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer) { + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -1842,26 +2636,89 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer) { + void sse_encode_list_near_action( + List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List(self is Uint8List ? self : Uint8List.fromList(self)); + for (final item in self) { + sse_encode_near_action(item, serializer); + } } @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer) { + void sse_encode_list_prim_u_8_loose( + List self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.length, serializer); + serializer.buffer.putUint8List( + self is Uint8List ? self : Uint8List.fromList(self), + ); + } + + @protected + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint8List(self); } @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer) { + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_list_prim_u_8_strict(self.sortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_strict(self.positions, serializer); } + @protected + void sse_encode_near_action(NearAction self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_near_action_kind(self.kind, serializer); + sse_encode_opt_String(self.target, serializer); + sse_encode_opt_list_prim_u_8_strict(self.args, serializer); + sse_encode_opt_box_autoadd_u_64(self.gas, serializer); + sse_encode_opt_U128(self.amount, serializer); + sse_encode_opt_String(self.publicKey, serializer); + sse_encode_bool(self.fullAccess, serializer); + sse_encode_list_String(self.methodNames, serializer); + sse_encode_opt_box_autoadd_u_32(self.codeLen, serializer); + } + + @protected + void sse_encode_near_action_kind( + NearActionKind self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_i_32(self.index, serializer); + } + + @protected + void sse_encode_near_transaction( + NearTransaction self, + SseSerializer serializer, + ) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.signerId, serializer); + sse_encode_String(self.publicKey, serializer); + sse_encode_list_prim_u_8_strict(self.publicKeyBytes, serializer); + sse_encode_bool(self.signsWithMlDsa65, serializer); + sse_encode_u_64(self.nonce, serializer); + sse_encode_String(self.receiverId, serializer); + sse_encode_String(self.blockHash, serializer); + sse_encode_list_prim_u_8_strict(self.hash, serializer); + sse_encode_list_near_action(self.actions, serializer); + } + @protected void sse_encode_opt_String(String? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1872,6 +2729,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } } + @protected + void sse_encode_opt_U128(BigInt? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_U128(self, serializer); + } + } + @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1883,7 +2750,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer) { + void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + + sse_encode_bool(self != null, serializer); + if (self != null) { + sse_encode_box_autoadd_u_64(self, serializer); + } + } + + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_bool(self != null, serializer); @@ -1975,14 +2855,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer) { + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.proofKind, serializer); sse_encode_String(self.proofHex, serializer); } @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer) { + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.address, serializer); sse_encode_list_prim_u_8_strict(self.firstHash, serializer); @@ -1993,15 +2879,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @sealed class HdLatticeErrorImpl extends RustOpaque implements HdLatticeError { // Not to be used by end users - HdLatticeErrorImpl.frbInternalDcoDecode(List wire) : super.frbInternalDcoDecode(wire, _kStaticData); + HdLatticeErrorImpl.frbInternalDcoDecode(List wire) + : super.frbInternalDcoDecode(wire, _kStaticData); // Not to be used by end users HdLatticeErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, - rustArcDecrementStrongCount: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, - rustArcDecrementStrongCountPtr: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, + rustArcIncrementStrongCount: + RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, + rustArcDecrementStrongCount: + RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, + rustArcDecrementStrongCountPtr: + RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, ); } diff --git a/quantus_sdk/lib/src/rust/frb_generated.io.dart b/quantus_sdk/lib/src/rust/frb_generated.io.dart index b115cd87f..67004831b 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.io.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.io.dart @@ -6,12 +6,14 @@ import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; +import 'api/near.dart'; import 'api/ur.dart'; import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'dart:ffi' as ffi; import 'frb_generated.dart'; +import 'lib.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart'; abstract class RustLibApiImplPlatform extends BaseApiImpl { @@ -22,20 +24,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ); @protected String dco_decode_String(dynamic raw); + @protected + BigInt dco_decode_U128(dynamic raw); + @protected AirdropMatch dco_decode_airdrop_match(dynamic raw); @@ -51,6 +61,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int dco_decode_box_autoadd_u_32(dynamic raw); + @protected + BigInt dco_decode_box_autoadd_u_64(dynamic raw); + @protected DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw); @@ -75,6 +88,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_list_prim_u_8_strict(dynamic raw); + @protected + List dco_decode_list_near_action(dynamic raw); + @protected List dco_decode_list_prim_u_8_loose(dynamic raw); @@ -84,12 +100,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed dco_decode_merkle_processed(dynamic raw); + @protected + NearAction dco_decode_near_action(dynamic raw); + + @protected + NearActionKind dco_decode_near_action_kind(dynamic raw); + + @protected + NearTransaction dco_decode_near_transaction(dynamic raw); + @protected String? dco_decode_opt_String(dynamic raw); + @protected + BigInt? dco_decode_opt_U128(dynamic raw); + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw); + @protected + BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw); + @protected Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw); @@ -130,18 +161,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected String sse_decode_String(SseDeserializer deserializer); + @protected + BigInt sse_decode_U128(SseDeserializer deserializer); + @protected AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer); @@ -158,7 +194,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); + BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer); + + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -176,10 +217,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match(SseDeserializer deserializer); + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ); @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_near_action(SseDeserializer deserializer); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -190,12 +238,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer); + @protected + NearAction sse_decode_near_action(SseDeserializer deserializer); + + @protected + NearActionKind sse_decode_near_action_kind(SseDeserializer deserializer); + + @protected + NearTransaction sse_decode_near_transaction(SseDeserializer deserializer); + @protected String? sse_decode_opt_String(SseDeserializer deserializer); + @protected + BigInt? sse_decode_opt_U128(SseDeserializer deserializer); + @protected int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer); + @protected + BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer); + @protected Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer); @@ -230,19 +293,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -250,6 +317,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_U128(BigInt self, SseSerializer serializer); + @protected void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer); @@ -260,16 +330,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); + void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ); + + @protected + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -284,28 +366,70 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match(List self, SseSerializer serializer); + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ); @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); + void sse_encode_list_near_action( + List self, + SseSerializer serializer, + ); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ); + + @protected + void sse_encode_near_action(NearAction self, SseSerializer serializer); @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); + void sse_encode_near_action_kind( + NearActionKind self, + SseSerializer serializer, + ); + + @protected + void sse_encode_near_transaction( + NearTransaction self, + SseSerializer serializer, + ); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); + @protected + void sse_encode_opt_U128(BigInt? self, SseSerializer serializer); + @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); + void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); + + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -338,24 +462,34 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ); @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ); } // Section: wire_class class RustLibWire implements BaseWire { - factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => RustLibWire(lib.ffiDynamicLibrary); + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => + RustLibWire(lib.ffiDynamicLibrary); /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) _lookup; + final ffi.Pointer Function(String symbolName) + _lookup; /// The symbols are looked up in [dynamicLibrary]. - RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + RustLibWire(ffi.DynamicLibrary dynamicLibrary) + : _lookup = dynamicLibrary.lookup; - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -371,7 +505,8 @@ class RustLibWire implements BaseWire { _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr .asFunction)>(); - void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( diff --git a/quantus_sdk/lib/src/rust/frb_generated.web.dart b/quantus_sdk/lib/src/rust/frb_generated.web.dart index f8299322b..5b7e6d217 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.web.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.web.dart @@ -9,11 +9,13 @@ import 'api/airdrop.dart'; import 'api/crypto.dart'; import 'api/multisig.dart'; +import 'api/near.dart'; import 'api/ur.dart'; import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'frb_generated.dart'; +import 'lib.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_web.dart'; abstract class RustLibApiImplPlatform extends BaseApiImpl { @@ -24,20 +26,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => - wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + CrossPlatformFinalizerArg + get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); + HdLatticeError + dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + dynamic raw, + ); @protected String dco_decode_String(dynamic raw); + @protected + BigInt dco_decode_U128(dynamic raw); + @protected AirdropMatch dco_decode_airdrop_match(dynamic raw); @@ -53,6 +63,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected int dco_decode_box_autoadd_u_32(dynamic raw); + @protected + BigInt dco_decode_box_autoadd_u_64(dynamic raw); + @protected DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw); @@ -77,6 +90,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected List dco_decode_list_list_prim_u_8_strict(dynamic raw); + @protected + List dco_decode_list_near_action(dynamic raw); + @protected List dco_decode_list_prim_u_8_loose(dynamic raw); @@ -86,12 +102,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed dco_decode_merkle_processed(dynamic raw); + @protected + NearAction dco_decode_near_action(dynamic raw); + + @protected + NearActionKind dco_decode_near_action_kind(dynamic raw); + + @protected + NearTransaction dco_decode_near_transaction(dynamic raw); + @protected String? dco_decode_opt_String(dynamic raw); + @protected + BigInt? dco_decode_opt_U128(dynamic raw); + @protected int? dco_decode_opt_box_autoadd_u_32(dynamic raw); + @protected + BigInt? dco_decode_opt_box_autoadd_u_64(dynamic raw); + @protected Uint8List? dco_decode_opt_list_prim_u_8_strict(dynamic raw); @@ -132,18 +163,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError + sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected String sse_decode_String(SseDeserializer deserializer); + @protected + BigInt sse_decode_U128(SseDeserializer deserializer); + @protected AirdropMatch sse_decode_airdrop_match(SseDeserializer deserializer); @@ -160,7 +196,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { int sse_decode_box_autoadd_u_32(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); + BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer); + + @protected + DilithiumClaimBody sse_decode_dilithium_claim_body( + SseDeserializer deserializer, + ); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -178,10 +219,17 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match(SseDeserializer deserializer); + List sse_decode_list_airdrop_match( + SseDeserializer deserializer, + ); @protected - List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); + List sse_decode_list_list_prim_u_8_strict( + SseDeserializer deserializer, + ); + + @protected + List sse_decode_list_near_action(SseDeserializer deserializer); @protected List sse_decode_list_prim_u_8_loose(SseDeserializer deserializer); @@ -192,12 +240,27 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected MerkleProcessed sse_decode_merkle_processed(SseDeserializer deserializer); + @protected + NearAction sse_decode_near_action(SseDeserializer deserializer); + + @protected + NearActionKind sse_decode_near_action_kind(SseDeserializer deserializer); + + @protected + NearTransaction sse_decode_near_transaction(SseDeserializer deserializer); + @protected String? sse_decode_opt_String(SseDeserializer deserializer); + @protected + BigInt? sse_decode_opt_U128(SseDeserializer deserializer); + @protected int? sse_decode_opt_box_autoadd_u_32(SseDeserializer deserializer); + @protected + BigInt? sse_decode_opt_box_autoadd_u_64(SseDeserializer deserializer); + @protected Uint8List? sse_decode_opt_list_prim_u_8_strict(SseDeserializer deserializer); @@ -232,19 +295,23 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); + WormholeClaimBody sse_decode_wormhole_claim_body( + SseDeserializer deserializer, + ); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -252,6 +319,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_String(String self, SseSerializer serializer); + @protected + void sse_encode_U128(BigInt self, SseSerializer serializer); + @protected void sse_encode_airdrop_match(AirdropMatch self, SseSerializer serializer); @@ -262,16 +332,28 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); + void sse_encode_box_autoadd_proof_input( + ProofInput self, + SseSerializer serializer, + ); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); + void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); + void sse_encode_dilithium_claim_body( + DilithiumClaimBody self, + SseSerializer serializer, + ); + + @protected + void sse_encode_dilithium_scheme( + DilithiumScheme self, + SseSerializer serializer, + ); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -286,28 +368,70 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match(List self, SseSerializer serializer); + void sse_encode_list_airdrop_match( + List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_list_list_prim_u_8_strict( + List self, + SseSerializer serializer, + ); @protected - void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); + void sse_encode_list_near_action( + List self, + SseSerializer serializer, + ); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); + void sse_encode_list_prim_u_8_strict( + Uint8List self, + SseSerializer serializer, + ); + + @protected + void sse_encode_merkle_processed( + MerkleProcessed self, + SseSerializer serializer, + ); + + @protected + void sse_encode_near_action(NearAction self, SseSerializer serializer); @protected - void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); + void sse_encode_near_action_kind( + NearActionKind self, + SseSerializer serializer, + ); + + @protected + void sse_encode_near_transaction( + NearTransaction self, + SseSerializer serializer, + ); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); + @protected + void sse_encode_opt_U128(BigInt? self, SseSerializer serializer); + @protected void sse_encode_opt_box_autoadd_u_32(int? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); + void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); + + @protected + void sse_encode_opt_list_prim_u_8_strict( + Uint8List? self, + SseSerializer serializer, + ); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -340,10 +464,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); + void sse_encode_wormhole_claim_body( + WormholeClaimBody self, + SseSerializer serializer, + ); @protected - void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); + void sse_encode_wormhole_result( + WormholeResult self, + SseSerializer serializer, + ); } // Section: wire_class @@ -351,14 +481,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ptr, ); - void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -373,8 +505,12 @@ external RustLibWasmModule get wasmModule; @anonymous extension type RustLibWasmModule._(JSObject _) implements JSObject { external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + int ptr, + ); external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + int ptr, + ); } diff --git a/quantus_sdk/lib/src/rust/lib.dart b/quantus_sdk/lib/src/rust/lib.dart new file mode 100644 index 000000000..a0d0fa1d6 --- /dev/null +++ b/quantus_sdk/lib/src/rust/lib.dart @@ -0,0 +1,20 @@ +// This file is automatically generated, so please do not edit it. +// @generated by `flutter_rust_bridge`@ 2.12.0. + +// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import + +import 'frb_generated.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; + +class U8Array32 extends NonGrowableListView { + static const arraySize = 32; + + @internal + Uint8List get inner => _inner; + final Uint8List _inner; + + U8Array32(this._inner) : assert(_inner.length == arraySize), super(_inner); + + U8Array32.init() : this(Uint8List(arraySize)); +} diff --git a/quantus_sdk/rust/Cargo.lock b/quantus_sdk/rust/Cargo.lock index 5a1daca99..6e9e0e853 100644 --- a/quantus_sdk/rust/Cargo.lock +++ b/quantus_sdk/rust/Cargo.lock @@ -654,6 +654,30 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "553c5d846a6ba5150c65e3b1b8ec073bcf1abc20f9b7220de384a4443ea4e20a" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cdfe656708a01f89b451a7d36466e6fe6c414de0aa18fc54f864f6f9ca9f56" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "bounded-collections" version = "0.3.2" @@ -729,6 +753,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "chacha20" version = "0.10.1" @@ -3358,6 +3388,8 @@ version = "0.1.0" dependencies = [ "anyhow", "blake3", + "borsh", + "bs58", "flutter_rust_bridge", "hex", "nam-tiny-hderive", @@ -3381,6 +3413,7 @@ dependencies = [ "quantus_ur", "rand_chacha 0.9.0", "sha2 0.10.9", + "sha3", "sp-core", "sp-runtime", "tempfile", diff --git a/quantus_sdk/rust/Cargo.toml b/quantus_sdk/rust/Cargo.toml index 882f2e64f..7ca05be8e 100644 --- a/quantus_sdk/rust/Cargo.toml +++ b/quantus_sdk/rust/Cargo.toml @@ -27,6 +27,13 @@ parity-scale-codec = { version = "3.7.5", default-features = false, features = [ sp-runtime = { version = "48.0.0", default-features = false, features = ["std"] } quantus_ur = { git = "https://github.com/Quantus-Network/quantus_ur.git", tag = "1.6.0" } +# NEAR transactions (borsh wire format, base58 key text, SHA-256 signing hash, +# SHA3-256 access-key handle). Aligned with quantus-cli's near module. +borsh = { version = "1.5", features = ["derive"] } +bs58 = "0.5" +sha2 = "0.10" +sha3 = "0.10" + # ZK proof generation (aligned with quantus-cli / chain) anyhow = "1.0" blake3 = "1.8" @@ -51,7 +58,6 @@ qp-zk-circuits-common = { version = "4.4.0", default-features = false, features qp-wormhole-circuit-builder = { version = "4.4.0" } [dev-dependencies] -sha2 = "0.10" tempfile = "3" # Circuit generation is CPU-intensive; without this it takes ~10min instead of ~30s. diff --git a/quantus_sdk/rust/src/api/mod.rs b/quantus_sdk/rust/src/api/mod.rs index 47a4a587e..355dd54e3 100644 --- a/quantus_sdk/rust/src/api/mod.rs +++ b/quantus_sdk/rust/src/api/mod.rs @@ -1,5 +1,6 @@ pub mod airdrop; pub mod crypto; pub mod multisig; +pub mod near; pub mod ur; pub mod wormhole; diff --git a/quantus_sdk/rust/src/api/near.rs b/quantus_sdk/rust/src/api/near.rs new file mode 100644 index 000000000..4cdb47518 --- /dev/null +++ b/quantus_sdk/rust/src/api/near.rs @@ -0,0 +1,685 @@ +//! NEAR transactions for the cold wallet: decode for display, sign with an +//! ML-DSA-65 account. +//! +//! NEAR accepts FIPS 204 ML-DSA-65 access keys and signatures from protocol +//! version 85. A cold-wallet request (signing envelope v2, see +//! `signing_request.dart`) carries the borsh-encoded `TransactionV0` raw, so +//! the device can show every action before signing. The device signs +//! `SHA-256(borsh(tx))` as a *pure* ML-DSA-65 signature with an **empty** +//! context — NEAR verifies without one, so [`crate::signing_context`] must +//! not be applied here — and answers `signature[3309] ‖ public_key[1952]`, +//! the same shape as a Quantus extrinsic response. +//! +//! The wire layout mirrors nearcore (`core/primitives/src/transaction.rs`, +//! `core/crypto/src/signature.rs`) and quantus-cli's `near::protocol`. Enum +//! variants are borsh u8 tags in declaration order; every position must stay. + +use crate::api::crypto::{DilithiumScheme, Keypair}; +use borsh::{BorshDeserialize, BorshSerialize}; +use qp_rusty_crystals_dilithium::ml_dsa_65; +use qp_rusty_crystals_hdwallet::SensitiveBytes32; +use sha2::{Digest, Sha256}; +use sha3::Sha3_256; + +pub const ML_DSA_65_PUBLIC_KEY_LEN: usize = 1952; +pub const ML_DSA_65_SIGNATURE_LEN: usize = 3309; + +/// Domain-separation tag nearcore hashes before the raw public key to form +/// the on-chain access-key handle (`ml-dsa-65-hash:` text form). +const ML_DSA_65_HANDLE_DOMAIN_TAG: &[u8] = b"near:ml-dsa-65-pubkey-hash:v1"; + +// ---- borsh wire types ------------------------------------------------------- + +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +enum WirePublicKey { + Ed25519([u8; 32]), + Secp256k1(Box<[u8; 64]>), + MlDsa65(Box<[u8; ML_DSA_65_PUBLIC_KEY_LEN]>), +} + +impl WirePublicKey { + fn text(&self) -> String { + match self { + WirePublicKey::Ed25519(b) => format!("ed25519:{}", bs58::encode(b).into_string()), + WirePublicKey::Secp256k1(b) => { + format!("secp256k1:{}", bs58::encode(b.as_ref()).into_string()) + } + WirePublicKey::MlDsa65(b) => { + format!("ml-dsa-65:{}", bs58::encode(b.as_ref()).into_string()) + } + } + } + + fn bytes(&self) -> Vec { + match self { + WirePublicKey::Ed25519(b) => b.to_vec(), + WirePublicKey::Secp256k1(b) => b.to_vec(), + WirePublicKey::MlDsa65(b) => b.to_vec(), + } + } +} + +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +struct WireAccessKey { + nonce: u64, + permission: WireAccessKeyPermission, +} + +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +enum WireAccessKeyPermission { + FunctionCall(WireFunctionCallPermission), + FullAccess, +} + +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +struct WireFunctionCallPermission { + allowance: Option, + receiver_id: String, + method_names: Vec, +} + +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +enum WireAction { + CreateAccount, + DeployContract { + code: Vec, + }, + FunctionCall { + method_name: String, + args: Vec, + gas: u64, + deposit: u128, + }, + Transfer { + deposit: u128, + }, + Stake { + stake: u128, + public_key: WirePublicKey, + }, + AddKey { + public_key: WirePublicKey, + access_key: WireAccessKey, + }, + DeleteKey { + public_key: WirePublicKey, + }, + DeleteAccount { + beneficiary_id: String, + }, +} + +/// nearcore `TransactionV0`: the signable body wallets produce. +#[flutter_rust_bridge::frb(ignore)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +struct WireTransaction { + signer_id: String, + public_key: WirePublicKey, + nonce: u64, + receiver_id: String, + block_hash: [u8; 32], + actions: Vec, +} + +// ---- Dart-facing model ------------------------------------------------------- + +/// The kind of a [`NearAction`]. Everything but `Transfer` and +/// `FunctionCall` changes who controls the account or what code it runs. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum NearActionKind { + CreateAccount, + DeployContract, + FunctionCall, + Transfer, + Stake, + AddKey, + DeleteKey, + DeleteAccount, +} + +/// One action of a NEAR transaction, flattened for display. Which fields are +/// set depends on [`NearAction::kind`]; amounts are yoctoNEAR (24 decimals) +/// and keys are NEAR text form (`:`). +/// +/// A flat struct rather than an enum with payloads: that keeps the Dart side +/// a plain class instead of a generated sealed hierarchy. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct NearAction { + pub kind: NearActionKind, + /// `FunctionCall`: the method; `DeleteAccount`: the beneficiary; + /// `AddKey` (function-call key): the contract the key may call. + pub target: Option, + /// `FunctionCall`: raw call arguments (usually JSON). + pub args: Option>, + /// `FunctionCall`: prepaid gas. + pub gas: Option, + /// `Transfer`/`FunctionCall`: attached deposit; `Stake`: staked amount; + /// `AddKey` (function-call key): gas allowance, `None` = unlimited. + pub amount: Option, + /// `Stake`/`AddKey`/`DeleteKey`: the key concerned. + pub public_key: Option, + /// `AddKey`: whether the key gets full access. + pub full_access: bool, + /// `AddKey` (function-call key): the methods it may call; empty = any. + pub method_names: Vec, + /// `DeployContract`: size of the code blob. + pub code_len: Option, +} + +impl Default for NearActionKind { + fn default() -> Self { + NearActionKind::Transfer + } +} + +impl From<&WireAction> for NearAction { + fn from(action: &WireAction) -> Self { + match action { + WireAction::CreateAccount => NearAction { + kind: NearActionKind::CreateAccount, + ..Default::default() + }, + WireAction::DeployContract { code } => NearAction { + kind: NearActionKind::DeployContract, + code_len: Some(code.len() as u32), + ..Default::default() + }, + WireAction::FunctionCall { + method_name, + args, + gas, + deposit, + } => NearAction { + kind: NearActionKind::FunctionCall, + target: Some(method_name.clone()), + args: Some(args.clone()), + gas: Some(*gas), + amount: Some(*deposit), + ..Default::default() + }, + WireAction::Transfer { deposit } => NearAction { + kind: NearActionKind::Transfer, + amount: Some(*deposit), + ..Default::default() + }, + WireAction::Stake { stake, public_key } => NearAction { + kind: NearActionKind::Stake, + amount: Some(*stake), + public_key: Some(public_key.text()), + ..Default::default() + }, + WireAction::AddKey { + public_key, + access_key, + } => match &access_key.permission { + WireAccessKeyPermission::FullAccess => NearAction { + kind: NearActionKind::AddKey, + public_key: Some(public_key.text()), + full_access: true, + ..Default::default() + }, + WireAccessKeyPermission::FunctionCall(p) => NearAction { + kind: NearActionKind::AddKey, + public_key: Some(public_key.text()), + full_access: false, + target: Some(p.receiver_id.clone()), + amount: p.allowance, + method_names: p.method_names.clone(), + ..Default::default() + }, + }, + WireAction::DeleteKey { public_key } => NearAction { + kind: NearActionKind::DeleteKey, + public_key: Some(public_key.text()), + ..Default::default() + }, + WireAction::DeleteAccount { beneficiary_id } => NearAction { + kind: NearActionKind::DeleteAccount, + target: Some(beneficiary_id.clone()), + ..Default::default() + }, + } + } +} + +/// A decoded NEAR transaction, ready to display. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NearTransaction { + pub signer_id: String, + /// The key that must sign, in NEAR text form. + pub public_key: String, + /// Raw bytes of that key; compare against a [`Keypair::public_key`]. + pub public_key_bytes: Vec, + /// Whether the signing key is ML-DSA-65 — the only scheme a Quantus + /// account can sign for. + pub signs_with_ml_dsa_65: bool, + pub nonce: u64, + pub receiver_id: String, + /// Base58, as NEAR tooling prints it. + pub block_hash: String, + /// SHA-256 of the transaction bytes: what gets signed, and the id the + /// NEAR RPC reports (as base58) once submitted. + pub hash: Vec, + pub actions: Vec, +} + +fn decode_wire(bytes: &[u8]) -> Result { + borsh::from_slice(bytes).map_err(|e| format!("Not a borsh NEAR transaction: {e}")) +} + +/// Decode a borsh `TransactionV0`. Refuses trailing bytes. +#[flutter_rust_bridge::frb(sync)] +pub fn decode_near_transaction(transaction: Vec) -> Result { + let wire = decode_wire(&transaction)?; + Ok(NearTransaction { + signer_id: wire.signer_id.clone(), + public_key: wire.public_key.text(), + public_key_bytes: wire.public_key.bytes(), + signs_with_ml_dsa_65: matches!(wire.public_key, WirePublicKey::MlDsa65(_)), + nonce: wire.nonce, + receiver_id: wire.receiver_id.clone(), + block_hash: bs58::encode(wire.block_hash).into_string(), + hash: Sha256::digest(&transaction).to_vec(), + actions: wire.actions.iter().map(NearAction::from).collect(), + }) +} + +/// SHA-256 of the transaction bytes: the message NEAR signs and verifies. +#[flutter_rust_bridge::frb(sync)] +pub fn near_transaction_hash(transaction: Vec) -> Vec { + Sha256::digest(&transaction).to_vec() +} + +fn require_ml_dsa_65(keypair: &Keypair) -> Result<(), String> { + if keypair.scheme != DilithiumScheme::MlDsa65 { + return Err("NEAR accepts ML-DSA-65 keys only; this account is ML-DSA-87".to_string()); + } + Ok(()) +} + +/// Sign a borsh `TransactionV0` for NEAR. Refuses unless `keypair` is +/// ML-DSA-65 *and* is the key the transaction declares. Returns +/// `signature ‖ public_key` (3309 + 1952 bytes). +#[flutter_rust_bridge::frb(sync)] +pub fn sign_near_transaction( + keypair: &Keypair, + transaction: Vec, + entropy: Option<[u8; 32]>, +) -> Result, String> { + require_ml_dsa_65(keypair)?; + let wire = decode_wire(&transaction)?; + if wire.public_key.bytes() != keypair.public_key { + return Err(format!( + "Transaction is signed by {}, not by this account", + wire.public_key.text() + )); + } + + let secret = ml_dsa_65::SecretKey::from_bytes(&keypair.secret_key) + .map_err(|e| format!("Secret key does not parse: {e:?}"))?; + let mut entropy = entropy; + let hedge = entropy.as_mut().map(SensitiveBytes32::new); + let hash = Sha256::digest(&transaction); + // Pure ML-DSA, no context: NEAR's verifier passes none. + let signature = secret + .sign(&hash, None, hedge.as_ref()) + .map_err(|e| format!("Signing failed: {e:?}"))?; + + let mut response = Vec::with_capacity(ML_DSA_65_SIGNATURE_LEN + ML_DSA_65_PUBLIC_KEY_LEN); + response.extend_from_slice(signature.as_ref()); + response.extend_from_slice(&keypair.public_key); + Ok(response) +} + +/// Whether `signature` is a valid pure ML-DSA-65 signature by `public_key` +/// over the transaction, as the NEAR runtime checks it. +#[flutter_rust_bridge::frb(sync)] +pub fn verify_near_signature( + public_key: Vec, + transaction: Vec, + signature: Vec, +) -> bool { + let Ok(public) = ml_dsa_65::PublicKey::from_bytes(&public_key) else { + return false; + }; + public.verify(&Sha256::digest(&transaction), &signature, None) +} + +/// The account's key in NEAR text form, `ml-dsa-65:`. This is the +/// `--signer-public-key` near-cli-rs takes and what an `AddKey` carries. +#[flutter_rust_bridge::frb(sync)] +pub fn near_public_key_text(keypair: &Keypair) -> Result { + require_ml_dsa_65(keypair)?; + Ok(format!( + "ml-dsa-65:{}", + bs58::encode(&keypair.public_key).into_string() + )) +} + +/// The on-chain handle of the key, `ml-dsa-65-hash:`: what +/// `view_access_key_list` shows, SHA3-256 of a domain tag and the key. +#[flutter_rust_bridge::frb(sync)] +pub fn near_public_key_handle(keypair: &Keypair) -> Result { + require_ml_dsa_65(keypair)?; + let mut hasher = Sha3_256::new(); + hasher.update(ML_DSA_65_HANDLE_DOMAIN_TAG); + hasher.update(&keypair.public_key); + Ok(format!( + "ml-dsa-65-hash:{}", + bs58::encode(hasher.finalize()).into_string() + )) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::api::crypto::generate_keypair_from_seed; + + /// near-cli-rs 0.30.1 `sign-later` output for a 1 NEAR transfer + /// alice.testnet → bob.testnet, ed25519 key 0x11×32, nonce 42, block hash + /// 0x22×32. The same vector pins quantus-cli's encoder. + const NEAR_CLI_UNSIGNED_B64: &str = "DQAAAGFsaWNlLnRlc3RuZXQAEREREREREREREREREREREREREREREREREREREREREREqAAAAAAAAAAsAAABib2IudGVzdG5ldCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiAQAAAAMAAACh7czOG8LTAAAAAAAA"; + const NEAR_CLI_HASH_HEX: &str = + "be358ec90e89b70256db586f72c4a280daa1199ba6f91398e9d034b72a7a6c9f"; + + fn b64(s: &str) -> Vec { + // Minimal base64 decoder: keeps the test free of a new dependency. + const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let mut out = Vec::new(); + let mut buf = 0u32; + let mut bits = 0; + for &c in s.as_bytes() { + if c == b'=' { + break; + } + let v = ALPHABET.iter().position(|&a| a == c).expect("base64 char") as u32; + buf = (buf << 6) | v; + bits += 6; + if bits >= 8 { + bits -= 8; + out.push((buf >> bits) as u8); + buf &= (1 << bits) - 1; + } + } + out + } + + fn ml_dsa_65_keypair(seed: u8) -> Keypair { + let mut seed = [seed; 32]; + let mut entropy = SensitiveBytes32::new(&mut seed); + let keypair = ml_dsa_65::Keypair::generate(&mut entropy); + Keypair { + public_key: keypair.public().to_bytes().to_vec(), + secret_key: keypair.secret().to_bytes().to_vec(), + scheme: DilithiumScheme::MlDsa65, + } + } + + fn tx_for(keypair: &Keypair) -> Vec { + let key: [u8; ML_DSA_65_PUBLIC_KEY_LEN] = keypair.public_key.as_slice().try_into().unwrap(); + borsh::to_vec(&WireTransaction { + signer_id: "vault.alice.near".to_string(), + public_key: WirePublicKey::MlDsa65(Box::new(key)), + nonce: 101, + receiver_id: "v2.ref-finance.near".to_string(), + block_hash: [0x22; 32], + actions: vec![WireAction::FunctionCall { + method_name: "storage_deposit".to_string(), + args: br#"{"registration_only":true}"#.to_vec(), + gas: 30_000_000_000_000, + deposit: 125 * 10u128.pow(21), + }], + }) + .unwrap() + } + + #[test] + fn decodes_near_cli_sign_later_output() { + let bytes = b64(NEAR_CLI_UNSIGNED_B64); + let tx = decode_near_transaction(bytes.clone()).unwrap(); + assert_eq!(tx.signer_id, "alice.testnet"); + assert_eq!(tx.receiver_id, "bob.testnet"); + assert_eq!(tx.nonce, 42); + assert_eq!( + tx.public_key, + "ed25519:29d2S7vB453rNYFdR5Ycwt7y9haRT5fwVwL9zTmBhfV2" + ); + assert_eq!(tx.public_key_bytes, vec![0x11; 32]); + assert!(!tx.signs_with_ml_dsa_65); + assert_eq!( + tx.block_hash, + "3JF3sEqM796hk5WFqA6EtmEwJQ9quALszsfJyvXNQKy3" + ); + assert_eq!(hex::encode(&tx.hash), NEAR_CLI_HASH_HEX); + assert_eq!( + tx.actions, + vec![NearAction { + kind: NearActionKind::Transfer, + amount: Some(10u128.pow(24)), + ..Default::default() + }] + ); + assert_eq!(near_transaction_hash(bytes), tx.hash); + } + + #[test] + fn rejects_trailing_and_truncated_bytes() { + let bytes = b64(NEAR_CLI_UNSIGNED_B64); + let mut extended = bytes.clone(); + extended.push(0); + assert!(decode_near_transaction(extended).is_err()); + assert!(decode_near_transaction(bytes[..bytes.len() - 1].to_vec()).is_err()); + assert!(decode_near_transaction(Vec::new()).is_err()); + } + + #[test] + fn decodes_every_action_kind() { + let key = WirePublicKey::MlDsa65(Box::new([0x33; ML_DSA_65_PUBLIC_KEY_LEN])); + let ed = WirePublicKey::Ed25519([0x11; 32]); + let bytes = borsh::to_vec(&WireTransaction { + signer_id: "a.near".to_string(), + public_key: key.clone(), + nonce: 1, + receiver_id: "b.near".to_string(), + block_hash: [0; 32], + actions: vec![ + WireAction::CreateAccount, + WireAction::DeployContract { code: vec![0; 10] }, + WireAction::Transfer { deposit: 5 }, + WireAction::Stake { + stake: 7, + public_key: ed.clone(), + }, + WireAction::AddKey { + public_key: key.clone(), + access_key: WireAccessKey { + nonce: 0, + permission: WireAccessKeyPermission::FullAccess, + }, + }, + WireAction::AddKey { + public_key: ed.clone(), + access_key: WireAccessKey { + nonce: 0, + permission: WireAccessKeyPermission::FunctionCall( + WireFunctionCallPermission { + allowance: Some(250), + receiver_id: "c.near".to_string(), + method_names: vec!["ping".to_string()], + }, + ), + }, + }, + WireAction::DeleteKey { + public_key: ed.clone(), + }, + WireAction::DeleteAccount { + beneficiary_id: "d.near".to_string(), + }, + ], + }) + .unwrap(); + + let tx = decode_near_transaction(bytes).unwrap(); + assert!(tx.signs_with_ml_dsa_65); + assert!(tx.public_key.starts_with("ml-dsa-65:")); + assert_eq!(tx.actions.len(), 8); + let kinds: Vec = tx.actions.iter().map(|a| a.kind).collect(); + assert_eq!( + kinds, + vec![ + NearActionKind::CreateAccount, + NearActionKind::DeployContract, + NearActionKind::Transfer, + NearActionKind::Stake, + NearActionKind::AddKey, + NearActionKind::AddKey, + NearActionKind::DeleteKey, + NearActionKind::DeleteAccount, + ] + ); + assert_eq!(tx.actions[1].code_len, Some(10)); + assert_eq!(tx.actions[2].amount, Some(5)); + assert_eq!( + (tx.actions[3].amount, tx.actions[3].public_key.as_deref()), + (Some(7), Some(ed.text().as_str())) + ); + assert_eq!( + tx.actions[4], + NearAction { + kind: NearActionKind::AddKey, + public_key: Some(key.text()), + full_access: true, + ..Default::default() + } + ); + assert_eq!( + tx.actions[5], + NearAction { + kind: NearActionKind::AddKey, + public_key: Some(ed.text()), + full_access: false, + target: Some("c.near".to_string()), + amount: Some(250), + method_names: vec!["ping".to_string()], + ..Default::default() + } + ); + assert_eq!(tx.actions[6].public_key, Some(ed.text())); + assert_eq!(tx.actions[7].target, Some("d.near".to_string())); + } + + #[test] + fn function_call_action_carries_method_args_gas_and_deposit() { + let keypair = ml_dsa_65_keypair(7); + let tx = decode_near_transaction(tx_for(&keypair)).unwrap(); + assert_eq!(tx.signer_id, "vault.alice.near"); + assert_eq!(tx.receiver_id, "v2.ref-finance.near"); + assert_eq!(tx.public_key_bytes, keypair.public_key); + assert!(tx.signs_with_ml_dsa_65); + assert_eq!( + tx.actions, + vec![NearAction { + kind: NearActionKind::FunctionCall, + target: Some("storage_deposit".to_string()), + args: Some(br#"{"registration_only":true}"#.to_vec()), + gas: Some(30_000_000_000_000), + amount: Some(125 * 10u128.pow(21)), + ..Default::default() + }] + ); + } + + #[test] + fn signs_pure_ml_dsa_65_over_the_hash_and_appends_the_key() { + let keypair = ml_dsa_65_keypair(7); + let tx = tx_for(&keypair); + + let response = sign_near_transaction(&keypair, tx.clone(), Some([9; 32])).unwrap(); + assert_eq!( + response.len(), + ML_DSA_65_SIGNATURE_LEN + ML_DSA_65_PUBLIC_KEY_LEN + ); + let (signature, public) = response.split_at(ML_DSA_65_SIGNATURE_LEN); + assert_eq!(public, keypair.public_key.as_slice()); + + assert!(verify_near_signature( + public.to_vec(), + tx.clone(), + signature.to_vec() + )); + + // Pure signature: verifies with no context and fails under the + // Quantus extrinsic context. + let verifier = ml_dsa_65::PublicKey::from_bytes(public).unwrap(); + let hash = Sha256::digest(&tx); + assert!(verifier.verify(&hash, signature, None)); + assert!(!verifier.verify(&hash, signature, Some(crate::signing_context::EXTRINSIC))); + + // Bound to this exact transaction. + let mut other = tx.clone(); + let nonce_offset = 4 + "vault.alice.near".len() + 1 + ML_DSA_65_PUBLIC_KEY_LEN; + other[nonce_offset] ^= 1; + assert!(!verify_near_signature( + public.to_vec(), + other, + signature.to_vec() + )); + + // Deterministic signing (no hedge) is also accepted. + let det = sign_near_transaction(&keypair, tx.clone(), None).unwrap(); + assert!(verify_near_signature( + keypair.public_key.clone(), + tx, + det[..ML_DSA_65_SIGNATURE_LEN].to_vec() + )); + } + + #[test] + fn refuses_other_keys_and_ml_dsa_87_accounts() { + let keypair = ml_dsa_65_keypair(7); + let other = ml_dsa_65_keypair(8); + let tx = tx_for(&keypair); + + let err = sign_near_transaction(&other, tx.clone(), None).unwrap_err(); + assert!(err.contains("not by this account"), "{err}"); + + let ml87 = generate_keypair_from_seed(vec![1; 32]); + assert_eq!(ml87.scheme, DilithiumScheme::MlDsa87); + let err = sign_near_transaction(&ml87, tx, None).unwrap_err(); + assert!(err.contains("ML-DSA-65"), "{err}"); + assert!(near_public_key_text(&ml87).is_err()); + assert!(near_public_key_handle(&ml87).is_err()); + } + + #[test] + fn key_text_and_handle_match_nearcore() { + let mut keypair = ml_dsa_65_keypair(7); + keypair.public_key = vec![0x42; ML_DSA_65_PUBLIC_KEY_LEN]; + + let text = near_public_key_text(&keypair).unwrap(); + assert!(text.starts_with("ml-dsa-65:")); + assert_eq!( + bs58::decode(&text["ml-dsa-65:".len()..]) + .into_vec() + .unwrap(), + keypair.public_key + ); + + // Golden from quantus-cli (SHA3-256 over tag ‖ key), cross-checked + // against near-cli-rs's "pub key hash" display. + let handle = near_public_key_handle(&keypair).unwrap(); + let handle_bytes = bs58::decode(&handle["ml-dsa-65-hash:".len()..]) + .into_vec() + .unwrap(); + assert_eq!( + hex::encode(handle_bytes), + "0ef8ccba4bb1a8859f1cc3d17c4d9d35712b8ddedd3875906559c50f7dd86505" + ); + } +} diff --git a/quantus_sdk/rust/src/frb_generated.rs b/quantus_sdk/rust/src/frb_generated.rs index 10d93ed92..d7880a038 100644 --- a/quantus_sdk/rust/src/frb_generated.rs +++ b/quantus_sdk/rust/src/frb_generated.rs @@ -39,7 +39,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueMoi, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.12.0"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1962506674; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -91816623; // Section: executor @@ -468,6 +468,36 @@ fn wire__crate__api__wormhole__decode_leaf_transfer_count_impl( }, ) } +fn wire__crate__api__near__decode_near_transaction_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "decode_near_transaction", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_transaction = >::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::near::decode_near_transaction(api_transaction)?; + Ok(output_ok) + })()) + }, + ) +} fn wire__crate__api__ur__decode_ur_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -922,6 +952,162 @@ fn wire__crate__api__ur__max_ur_parts_impl( }, ) } +fn wire__crate__api__near__near_action_default_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "near_action_default", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::near::NearAction::default())?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__near__near_action_kind_default_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "near_action_kind_default", + port: Some(port_), + mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + deserializer.end(); + move |context| { + transform_result_sse::<_, ()>((move || { + let output_ok = + Result::<_, ()>::Ok(crate::api::near::NearActionKind::default())?; + Ok(output_ok) + })()) + } + }, + ) +} +fn wire__crate__api__near__near_public_key_handle_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "near_public_key_handle", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_keypair = ::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::near::near_public_key_handle(&api_keypair)?; + Ok(output_ok) + })()) + }, + ) +} +fn wire__crate__api__near__near_public_key_text_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "near_public_key_text", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_keypair = ::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::near::near_public_key_text(&api_keypair)?; + Ok(output_ok) + })()) + }, + ) +} +fn wire__crate__api__near__near_transaction_hash_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "near_transaction_hash", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_transaction = >::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, ()>((move || { + let output_ok = + Result::<_, ()>::Ok(crate::api::near::near_transaction_hash(api_transaction))?; + Ok(output_ok) + })()) + }, + ) +} fn wire__crate__api__multisig__predict_multisig_address_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1165,6 +1351,42 @@ fn wire__crate__api__crypto__sign_message_with_pubkey_impl( }, ) } +fn wire__crate__api__near__sign_near_transaction_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "sign_near_transaction", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_keypair = ::sse_decode(&mut deserializer); + let api_transaction = >::sse_decode(&mut deserializer); + let api_entropy = >::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, String>((move || { + let output_ok = crate::api::near::sign_near_transaction( + &api_keypair, + api_transaction, + api_entropy, + )?; + Ok(output_ok) + })()) + }, + ) +} fn wire__crate__api__crypto__signature_bytes_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1294,6 +1516,42 @@ fn wire__crate__api__crypto__verify_message_impl( }, ) } +fn wire__crate__api__near__verify_near_signature_impl( + ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, + rust_vec_len_: i32, + data_len_: i32, +) -> flutter_rust_bridge::for_generated::WireSyncRust2DartSse { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_sync::( + flutter_rust_bridge::for_generated::TaskInfo { + debug_name: "verify_near_signature", + port: None, + mode: flutter_rust_bridge::for_generated::FfiCallMode::Sync, + }, + move || { + let message = unsafe { + flutter_rust_bridge::for_generated::Dart2RustMessageSse::from_wire( + ptr_, + rust_vec_len_, + data_len_, + ) + }; + let mut deserializer = + flutter_rust_bridge::for_generated::SseDeserializer::new(message); + let api_public_key = >::sse_decode(&mut deserializer); + let api_transaction = >::sse_decode(&mut deserializer); + let api_signature = >::sse_decode(&mut deserializer); + deserializer.end(); + transform_result_sse::<_, ()>((move || { + let output_ok = Result::<_, ()>::Ok(crate::api::near::verify_near_signature( + api_public_key, + api_transaction, + api_signature, + ))?; + Ok(output_ok) + })()) + }, + ) +} fn wire__crate__api__wormhole__wormhole_compute_output_amount_impl( ptr_: flutter_rust_bridge::for_generated::PlatformGeneralizedUint8ListPtr, rust_vec_len_: i32, @@ -1394,6 +1652,14 @@ impl SseDecode for String { } } +impl SseDecode for u128 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return inner.parse().unwrap(); + } +} + impl SseDecode for crate::api::airdrop::AirdropMatch { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1521,6 +1787,18 @@ impl SseDecode for Vec> { } } +impl SseDecode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut len_ = ::sse_decode(deserializer); + let mut ans_ = Vec::with_capacity(len_ as usize); + for idx_ in 0..len_ { + ans_.push(::sse_decode(deserializer)); + } + return ans_; + } +} + impl SseDecode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1545,6 +1823,76 @@ impl SseDecode for crate::api::wormhole::MerkleProcessed { } } +impl SseDecode for crate::api::near::NearAction { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_kind = ::sse_decode(deserializer); + let mut var_target = >::sse_decode(deserializer); + let mut var_args = >>::sse_decode(deserializer); + let mut var_gas = >::sse_decode(deserializer); + let mut var_amount = >::sse_decode(deserializer); + let mut var_publicKey = >::sse_decode(deserializer); + let mut var_fullAccess = ::sse_decode(deserializer); + let mut var_methodNames = >::sse_decode(deserializer); + let mut var_codeLen = >::sse_decode(deserializer); + return crate::api::near::NearAction { + kind: var_kind, + target: var_target, + args: var_args, + gas: var_gas, + amount: var_amount, + public_key: var_publicKey, + full_access: var_fullAccess, + method_names: var_methodNames, + code_len: var_codeLen, + }; + } +} + +impl SseDecode for crate::api::near::NearActionKind { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return match inner { + 0 => crate::api::near::NearActionKind::CreateAccount, + 1 => crate::api::near::NearActionKind::DeployContract, + 2 => crate::api::near::NearActionKind::FunctionCall, + 3 => crate::api::near::NearActionKind::Transfer, + 4 => crate::api::near::NearActionKind::Stake, + 5 => crate::api::near::NearActionKind::AddKey, + 6 => crate::api::near::NearActionKind::DeleteKey, + 7 => crate::api::near::NearActionKind::DeleteAccount, + _ => unreachable!("Invalid variant for NearActionKind: {}", inner), + }; + } +} + +impl SseDecode for crate::api::near::NearTransaction { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_signerId = ::sse_decode(deserializer); + let mut var_publicKey = ::sse_decode(deserializer); + let mut var_publicKeyBytes = >::sse_decode(deserializer); + let mut var_signsWithMlDsa65 = ::sse_decode(deserializer); + let mut var_nonce = ::sse_decode(deserializer); + let mut var_receiverId = ::sse_decode(deserializer); + let mut var_blockHash = ::sse_decode(deserializer); + let mut var_hash = >::sse_decode(deserializer); + let mut var_actions = >::sse_decode(deserializer); + return crate::api::near::NearTransaction { + signer_id: var_signerId, + public_key: var_publicKey, + public_key_bytes: var_publicKeyBytes, + signs_with_ml_dsa_65: var_signsWithMlDsa65, + nonce: var_nonce, + receiver_id: var_receiverId, + block_hash: var_blockHash, + hash: var_hash, + actions: var_actions, + }; + } +} + impl SseDecode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1556,6 +1904,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1567,6 +1926,17 @@ impl SseDecode for Option { } } +impl SseDecode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + if (::sse_decode(deserializer)) { + return Some(::sse_decode(deserializer)); + } else { + return None; + } + } +} + impl SseDecode for Option> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1743,18 +2113,22 @@ fn pde_ffi_dispatcher_primary_impl( rust_vec_len, data_len, ), - 17 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( + 18 => wire__crate__api__wormhole__ensure_circuit_binaries_impl( port, ptr, rust_vec_len, data_len, ), - 18 => { + 19 => { wire__crate__api__airdrop__find_airdrop_matches_impl(port, ptr, rust_vec_len, data_len) } - 23 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), - 24 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), - 29 => wire__crate__api__airdrop__prove_airdrop_wormhole_impl( + 24 => wire__crate__api__wormhole__generate_proof_impl(port, ptr, rust_vec_len, data_len), + 25 => wire__crate__api__crypto__init_app_impl(port, ptr, rust_vec_len, data_len), + 29 => wire__crate__api__near__near_action_default_impl(port, ptr, rust_vec_len, data_len), + 30 => { + wire__crate__api__near__near_action_kind_default_impl(port, ptr, rust_vec_len, data_len) + } + 35 => wire__crate__api__airdrop__prove_airdrop_wormhole_impl( port, ptr, rust_vec_len, @@ -1784,36 +2158,42 @@ fn pde_ffi_dispatcher_sync_impl( 13 => { wire__crate__api__wormhole__decode_leaf_transfer_count_impl(ptr, rust_vec_len, data_len) } - 14 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), - 15 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), - 16 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), - 19 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), - 20 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), - 21 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), - 22 => { + 14 => wire__crate__api__near__decode_near_transaction_impl(ptr, rust_vec_len, data_len), + 15 => wire__crate__api__ur__decode_ur_impl(ptr, rust_vec_len, data_len), + 16 => wire__crate__api__crypto__derive_wormhole_impl(ptr, rust_vec_len, data_len), + 17 => wire__crate__api__ur__encode_ur_impl(ptr, rust_vec_len, data_len), + 20 => wire__crate__api__crypto__first_hash_to_address_impl(ptr, rust_vec_len, data_len), + 21 => wire__crate__api__crypto__generate_derived_keypair_impl(ptr, rust_vec_len, data_len), + 22 => wire__crate__api__crypto__generate_keypair_impl(ptr, rust_vec_len, data_len), + 23 => { wire__crate__api__crypto__generate_keypair_from_seed_impl(ptr, rust_vec_len, data_len) } - 25 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), - 26 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), - 27 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), - 28 => { + 26 => wire__crate__api__ur__is_complete_ur_impl(ptr, rust_vec_len, data_len), + 27 => wire__crate__api__ur__max_ur_part_chars_impl(ptr, rust_vec_len, data_len), + 28 => wire__crate__api__ur__max_ur_parts_impl(ptr, rust_vec_len, data_len), + 31 => wire__crate__api__near__near_public_key_handle_impl(ptr, rust_vec_len, data_len), + 32 => wire__crate__api__near__near_public_key_text_impl(ptr, rust_vec_len, data_len), + 33 => wire__crate__api__near__near_transaction_hash_impl(ptr, rust_vec_len, data_len), + 34 => { wire__crate__api__multisig__predict_multisig_address_impl(ptr, rust_vec_len, data_len) } - 30 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), - 31 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), - 32 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), - 33 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), - 34 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), - 35 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), - 36 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), - 37 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), - 38 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), - 39 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( + 36 => wire__crate__api__crypto__public_key_bytes_impl(ptr, rust_vec_len, data_len), + 37 => wire__crate__api__crypto__secret_key_bytes_impl(ptr, rust_vec_len, data_len), + 38 => wire__crate__api__crypto__set_default_ss58_prefix_impl(ptr, rust_vec_len, data_len), + 39 => wire__crate__api__crypto__sign_message_impl(ptr, rust_vec_len, data_len), + 40 => wire__crate__api__crypto__sign_message_with_pubkey_impl(ptr, rust_vec_len, data_len), + 41 => wire__crate__api__near__sign_near_transaction_impl(ptr, rust_vec_len, data_len), + 42 => wire__crate__api__crypto__signature_bytes_impl(ptr, rust_vec_len, data_len), + 43 => wire__crate__api__crypto__ss58_to_account_id_impl(ptr, rust_vec_len, data_len), + 44 => wire__crate__api__crypto__to_account_id_impl(ptr, rust_vec_len, data_len), + 45 => wire__crate__api__crypto__verify_message_impl(ptr, rust_vec_len, data_len), + 46 => wire__crate__api__near__verify_near_signature_impl(ptr, rust_vec_len, data_len), + 47 => wire__crate__api__wormhole__wormhole_compute_output_amount_impl( ptr, rust_vec_len, data_len, ), - 40 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), + 48 => wire__crate__api__wormhole__zk_circuits_version_impl(ptr, rust_vec_len, data_len), _ => unreachable!(), } } @@ -1948,6 +2328,86 @@ impl flutter_rust_bridge::IntoIntoDart } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::near::NearAction { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.kind.into_into_dart().into_dart(), + self.target.into_into_dart().into_dart(), + self.args.into_into_dart().into_dart(), + self.gas.into_into_dart().into_dart(), + self.amount.into_into_dart().into_dart(), + self.public_key.into_into_dart().into_dart(), + self.full_access.into_into_dart().into_dart(), + self.method_names.into_into_dart().into_dart(), + self.code_len.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::api::near::NearAction {} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::near::NearAction +{ + fn into_into_dart(self) -> crate::api::near::NearAction { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::near::NearActionKind { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + match self { + Self::CreateAccount => 0.into_dart(), + Self::DeployContract => 1.into_dart(), + Self::FunctionCall => 2.into_dart(), + Self::Transfer => 3.into_dart(), + Self::Stake => 4.into_dart(), + Self::AddKey => 5.into_dart(), + Self::DeleteKey => 6.into_dart(), + Self::DeleteAccount => 7.into_dart(), + _ => unreachable!(), + } + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::near::NearActionKind +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::near::NearActionKind +{ + fn into_into_dart(self) -> crate::api::near::NearActionKind { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::api::near::NearTransaction { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.signer_id.into_into_dart().into_dart(), + self.public_key.into_into_dart().into_dart(), + self.public_key_bytes.into_into_dart().into_dart(), + self.signs_with_ml_dsa_65.into_into_dart().into_dart(), + self.nonce.into_into_dart().into_dart(), + self.receiver_id.into_into_dart().into_dart(), + self.block_hash.into_into_dart().into_dart(), + self.hash.into_into_dart().into_dart(), + self.actions.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive + for crate::api::near::NearTransaction +{ +} +impl flutter_rust_bridge::IntoIntoDart + for crate::api::near::NearTransaction +{ + fn into_into_dart(self) -> crate::api::near::NearTransaction { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::api::wormhole::ProofInput { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ @@ -2075,6 +2535,13 @@ impl SseEncode for String { } } +impl SseEncode for u128 { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.to_string(), serializer); + } +} + impl SseEncode for crate::api::airdrop::AirdropMatch { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2176,6 +2643,16 @@ impl SseEncode for Vec> { } } +impl SseEncode for Vec { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.len() as _, serializer); + for item in self { + ::sse_encode(item, serializer); + } + } +} + impl SseEncode for Vec { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2194,6 +2671,58 @@ impl SseEncode for crate::api::wormhole::MerkleProcessed { } } +impl SseEncode for crate::api::near::NearAction { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.kind, serializer); + >::sse_encode(self.target, serializer); + >>::sse_encode(self.args, serializer); + >::sse_encode(self.gas, serializer); + >::sse_encode(self.amount, serializer); + >::sse_encode(self.public_key, serializer); + ::sse_encode(self.full_access, serializer); + >::sse_encode(self.method_names, serializer); + >::sse_encode(self.code_len, serializer); + } +} + +impl SseEncode for crate::api::near::NearActionKind { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode( + match self { + crate::api::near::NearActionKind::CreateAccount => 0, + crate::api::near::NearActionKind::DeployContract => 1, + crate::api::near::NearActionKind::FunctionCall => 2, + crate::api::near::NearActionKind::Transfer => 3, + crate::api::near::NearActionKind::Stake => 4, + crate::api::near::NearActionKind::AddKey => 5, + crate::api::near::NearActionKind::DeleteKey => 6, + crate::api::near::NearActionKind::DeleteAccount => 7, + _ => { + unimplemented!(""); + } + }, + serializer, + ); + } +} + +impl SseEncode for crate::api::near::NearTransaction { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.signer_id, serializer); + ::sse_encode(self.public_key, serializer); + >::sse_encode(self.public_key_bytes, serializer); + ::sse_encode(self.signs_with_ml_dsa_65, serializer); + ::sse_encode(self.nonce, serializer); + ::sse_encode(self.receiver_id, serializer); + ::sse_encode(self.block_hash, serializer); + >::sse_encode(self.hash, serializer); + >::sse_encode(self.actions, serializer); + } +} + impl SseEncode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2204,6 +2733,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -2214,6 +2753,16 @@ impl SseEncode for Option { } } +impl SseEncode for Option { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.is_some(), serializer); + if let Some(value) = self { + ::sse_encode(value, serializer); + } + } +} + impl SseEncode for Option> { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { From 6db5866a67f03dcb29666849832704594acae0ab Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:02:29 +0000 Subject: [PATCH 02/16] sdk: NEAR signing request envelope (v2) and NEAR public key export NearSigningRequest is envelope version 2 of the ur:quantus-sign-request payload: {v:2, chain:'near', network, payload:0x} with a strict key set and the existing 8 KiB cap. AnySigningRequest.decode dispatches on v so a v1 wallet refuses v2 by version as before. NearPublicKeyExport is the self-describing JSON a cold wallet shows when publishing an ML-DSA-65 key for NEAR. --- .../src/models/near_public_key_export.dart | 51 +++++++ .../lib/src/models/signing_request.dart | 143 +++++++++++++----- .../models/near_public_key_export_test.dart | 41 +++++ .../test/models/signing_request_test.dart | 47 ++++++ 4 files changed, 247 insertions(+), 35 deletions(-) create mode 100644 quantus_sdk/lib/src/models/near_public_key_export.dart create mode 100644 quantus_sdk/test/models/near_public_key_export_test.dart diff --git a/quantus_sdk/lib/src/models/near_public_key_export.dart b/quantus_sdk/lib/src/models/near_public_key_export.dart new file mode 100644 index 000000000..ceb24fc6d --- /dev/null +++ b/quantus_sdk/lib/src/models/near_public_key_export.dart @@ -0,0 +1,51 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +/// A cold wallet's ML-DSA-65 key published for use on NEAR, as the cold +/// wallet shows it in a QR and a hot wallet or CLI reads it. +/// +/// [address] is the Quantus SS58 address of the account that holds the key, +/// so the reader can tie the NEAR key to a cold account it already knows; +/// [nearPublicKey] is the key in NEAR's `ml-dsa-65:` text form, ready +/// for `near account add-key` and for a signing request's `--signer-public-key`. +class NearPublicKeyExport { + static const int version = 1; + static const String kind = 'near-public-key'; + static const String nearKeyPrefix = 'ml-dsa-65:'; + + final String address; + final String nearPublicKey; + + const NearPublicKeyExport({required this.address, required this.nearPublicKey}); + + Uint8List encode() => Uint8List.fromList( + utf8.encode(jsonEncode({'v': version, 'kind': kind, 'address': address, 'near_public_key': nearPublicKey})), + ); + + /// Throws [FormatException] on anything that is not exactly one export. + static NearPublicKeyExport decode(Uint8List bytes) { + final Object? json; + try { + json = jsonDecode(utf8.decode(bytes)); + } catch (e) { + throw FormatException('Not a NEAR public key export: $e'); + } + if (json is! Map) throw const FormatException('NEAR public key export is not a JSON object'); + final keys = json.keys.map((k) => '$k').toSet(); + const expected = {'v', 'kind', 'address', 'near_public_key'}; + if (keys.length != expected.length || !keys.containsAll(expected)) { + throw FormatException( + 'NEAR public key export keys ${keys.toList()..sort()} are not ${expected.toList()..sort()}', + ); + } + if (json['v'] != version) throw FormatException('NEAR public key export version ${json['v']} is not $version'); + if (json['kind'] != kind) throw FormatException('NEAR public key export kind ${json['kind']} is not $kind'); + final address = json['address']; + final nearPublicKey = json['near_public_key']; + if (address is! String || address.isEmpty) throw const FormatException('NEAR public key export address missing'); + if (nearPublicKey is! String || !nearPublicKey.startsWith(nearKeyPrefix)) { + throw const FormatException('NEAR public key export key is not $nearKeyPrefix…'); + } + return NearPublicKeyExport(address: address, nearPublicKey: nearPublicKey); + } +} diff --git a/quantus_sdk/lib/src/models/signing_request.dart b/quantus_sdk/lib/src/models/signing_request.dart index eac8b1db6..618d142b6 100644 --- a/quantus_sdk/lib/src/models/signing_request.dart +++ b/quantus_sdk/lib/src/models/signing_request.dart @@ -11,7 +11,110 @@ class UnsupportedSigningRequestVersionException extends FormatException { final Object? requested; UnsupportedSigningRequestVersionException(this.requested) - : super('Signing request version $requested is not supported, this app reads version ${SigningRequest.version}'); + : super( + 'Signing request version $requested is not supported, this app reads versions ' + '${SigningRequest.version} and ${NearSigningRequest.version}', + ); +} + +/// Any signing request this app reads, dispatched on the envelope's `v`. +/// +/// Version 1 ([SigningRequest]) carries a Quantus signing payload for a named +/// SS58 account; version 2 ([NearSigningRequest]) carries a NEAR transaction, +/// which names its own signer and key. Both travel in the same +/// `ur:quantus-sign-request` UR, so a wallet built before v2 refuses it by +/// version, exactly as it refuses any version it does not know. +sealed class AnySigningRequest { + const AnySigningRequest(); + + /// Throws [FormatException] on anything that is not exactly one envelope. + static AnySigningRequest decode(Uint8List bytes) { + final json = _decodeJsonObject(bytes); + final version = json['v']; + return switch (version) { + SigningRequest.version => SigningRequest.decode(bytes), + NearSigningRequest.version => NearSigningRequest.decode(bytes), + _ => throw UnsupportedSigningRequestVersionException(version), + }; + } +} + +Map _decodeJsonObject(Uint8List bytes) { + final Object? json; + try { + json = jsonDecode(utf8.decode(bytes)); + } catch (e) { + throw FormatException('Not a signing request: $e'); + } + if (json is! Map) throw const FormatException('Signing request is not a JSON object'); + return json; +} + +void _requireExactKeys(Map json, Set keys) { + final actual = json.keys.map((k) => '$k').toSet(); + if (!actual.containsAll(keys) || actual.length != keys.length) { + throw FormatException('Signing request keys ${actual.toList()..sort()} are not ${keys.toList()..sort()}'); + } +} + +Uint8List _decodePayload(Object? payload) { + if (payload is! String || !payload.startsWith('0x')) { + throw const FormatException('Signing request payload is not 0x hex'); + } + final payloadHex = payload.substring(2); + if (payloadHex.length > maxPayloadBytes * 2) { + throw const FormatException('Signing request payload too large: more than $maxPayloadBytes bytes'); + } + final Uint8List bytesOut; + try { + bytesOut = Uint8List.fromList(hex.decode(payloadHex)); + } catch (e) { + throw FormatException('Signing request payload is not hex: $e'); + } + if (bytesOut.isEmpty) throw const FormatException('Signing request payload is empty'); + if (bytesOut.length > maxPayloadBytes) { + throw FormatException('Signing request payload too large: ${bytesOut.length} bytes'); + } + return bytesOut; +} + +/// A NEAR transaction for this wallet to sign (envelope version 2). +/// +/// The payload is the borsh-encoded NEAR `TransactionV0`, sent raw so the +/// signer can decode and display every action. It already names the signer +/// account and the exact public key that must sign, so the envelope carries no +/// `signer`; the device matches that key against its own ML-DSA-65 accounts. +/// [network] is for display only — NEAR transactions carry no chain id, and +/// nothing in it is signed. +class NearSigningRequest extends AnySigningRequest { + static const int version = 2; + static const String chain = 'near'; + + final String network; + final Uint8List transaction; + + const NearSigningRequest({required this.network, required this.transaction}); + + Uint8List encode() => Uint8List.fromList( + utf8.encode( + jsonEncode({'v': version, 'chain': chain, 'network': network, 'payload': '0x${hex.encode(transaction)}'}), + ), + ); + + /// Throws [FormatException] on anything that is not exactly this envelope. + static NearSigningRequest decode(Uint8List bytes) { + final json = _decodeJsonObject(bytes); + _requireExactKeys(json, const {'v', 'chain', 'network', 'payload'}); + if (json['v'] != version) throw UnsupportedSigningRequestVersionException(json['v']); + if (json['chain'] != chain) throw FormatException('Signing request is for chain ${json['chain']}, not NEAR'); + + final network = json['network']; + if (network is! String || network.isEmpty) { + throw const FormatException('Signing request names no NEAR network'); + } + + return NearSigningRequest(network: network, transaction: _decodePayload(json['payload'])); + } } /// A signing payload together with the account that must sign it. @@ -19,7 +122,7 @@ class UnsupportedSigningRequestVersionException extends FormatException { /// The payload alone says nothing about which key it belongs to, so a signer /// holding several accounts cannot tell which to use, and one holding the wrong /// account cannot tell that it does. -class SigningRequest { +class SigningRequest extends AnySigningRequest { static const int version = 1; final String signer; @@ -33,19 +136,8 @@ class SigningRequest { /// Throws [FormatException] on anything that is not exactly this envelope. static SigningRequest decode(Uint8List bytes) { - final Object? json; - try { - json = jsonDecode(utf8.decode(bytes)); - } catch (e) { - throw FormatException('Not a signing request: $e'); - } - if (json is! Map) throw const FormatException('Signing request is not a JSON object'); - - const keys = {'v', 'signer', 'payload'}; - final actual = json.keys.map((k) => '$k').toSet(); - if (!actual.containsAll(keys) || actual.length != keys.length) { - throw FormatException('Signing request keys ${actual.toList()..sort()} are not ${keys.toList()..sort()}'); - } + final json = _decodeJsonObject(bytes); + _requireExactKeys(json, const {'v', 'signer', 'payload'}); if (json['v'] != version) throw UnsupportedSigningRequestVersionException(json['v']); final signer = json['signer']; @@ -56,25 +148,6 @@ class SigningRequest { throw FormatException('Signing request signer is not a valid address: $e'); } - final payload = json['payload']; - if (payload is! String || !payload.startsWith('0x')) { - throw const FormatException('Signing request payload is not 0x hex'); - } - final payloadHex = payload.substring(2); - if (payloadHex.length > maxPayloadBytes * 2) { - throw const FormatException('Signing request payload too large: more than $maxPayloadBytes bytes'); - } - final Uint8List bytesOut; - try { - bytesOut = Uint8List.fromList(hex.decode(payloadHex)); - } catch (e) { - throw FormatException('Signing request payload is not hex: $e'); - } - if (bytesOut.isEmpty) throw const FormatException('Signing request payload is empty'); - if (bytesOut.length > maxPayloadBytes) { - throw FormatException('Signing request payload too large: ${bytesOut.length} bytes'); - } - - return SigningRequest(signer: signer, payload: bytesOut); + return SigningRequest(signer: signer, payload: _decodePayload(json['payload'])); } } diff --git a/quantus_sdk/test/models/near_public_key_export_test.dart b/quantus_sdk/test/models/near_public_key_export_test.dart new file mode 100644 index 000000000..8ef40beaf --- /dev/null +++ b/quantus_sdk/test/models/near_public_key_export_test.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +void main() { + const address = 'qzmNaLjPU7hcvkjHpGmrVDPD9y12vdAFimCSrP1GkhVFJMaUq'; + const key = 'ml-dsa-65:3xTqD5Fh2kLm9nPq'; + + group('NearPublicKeyExport', () { + test('encodes to the self-describing JSON a reader dispatches on', () { + final json = jsonDecode(utf8.decode(const NearPublicKeyExport(address: address, nearPublicKey: key).encode())); + expect(json, {'v': 1, 'kind': 'near-public-key', 'address': address, 'near_public_key': key}); + }); + + test('round-trips', () { + const export = NearPublicKeyExport(address: address, nearPublicKey: key); + final decoded = NearPublicKeyExport.decode(export.encode()); + expect(decoded.address, address); + expect(decoded.nearPublicKey, key); + }); + + test('rejects a wrong version, kind, key prefix, missing or extra key, and non-JSON', () { + Uint8List bytes(Map json) => Uint8List.fromList(utf8.encode(jsonEncode(json))); + const good = {'v': 1, 'kind': 'near-public-key', 'address': address, 'near_public_key': key}; + for (final bad in [ + {...good, 'v': 2}, + {...good, 'kind': 'quantus-public-key'}, + {...good, 'near_public_key': 'ed25519:abc'}, + {...good, 'address': ''}, + {...good}..remove('address'), + {...good, 'extra': 1}, + ]) { + expect(() => NearPublicKeyExport.decode(bytes(bad)), throwsFormatException, reason: '$bad'); + } + expect(() => NearPublicKeyExport.decode(Uint8List.fromList([0xff, 0x00])), throwsFormatException); + expect(() => NearPublicKeyExport.decode(Uint8List.fromList(utf8.encode('[1]'))), throwsFormatException); + }); + }); +} diff --git a/quantus_sdk/test/models/signing_request_test.dart b/quantus_sdk/test/models/signing_request_test.dart index c82c8414f..6197d0b51 100644 --- a/quantus_sdk/test/models/signing_request_test.dart +++ b/quantus_sdk/test/models/signing_request_test.dart @@ -75,4 +75,51 @@ void main() { ); }); }); + + group('NearSigningRequest (version 2)', () { + final transaction = Uint8List.fromList([0x0d, 0, 0, 0, 0x61]); + final nearJson = {'v': 2, 'chain': 'near', 'network': 'mainnet', 'payload': '0x0d00000061'}; + + test('round-trips the network and the transaction', () { + final decoded = NearSigningRequest.decode( + NearSigningRequest(network: 'testnet', transaction: transaction).encode(), + ); + + expect(decoded.network, 'testnet'); + expect(decoded.transaction, transaction); + }); + + test('carries exactly the keys the CLI writes', () { + final json = jsonDecode(utf8.decode(NearSigningRequest(network: 'mainnet', transaction: transaction).encode())); + + expect(json, nearJson); + }); + + test('is what AnySigningRequest.decode returns for v2, and v1 stays v1', () { + final near = AnySigningRequest.decode(envelope(nearJson)); + expect(near, isA().having((r) => r.network, 'network', 'mainnet')); + + final quantus = AnySigningRequest.decode(SigningRequest(signer: signer, payload: payload).encode()); + expect(quantus, isA().having((r) => r.signer, 'signer', signer)); + + expect( + () => AnySigningRequest.decode(envelope({'v': 3, 'payload': '0x00'})), + throwsA(isA().having((e) => e.requested, 'requested', 3)), + ); + expect(() => AnySigningRequest.decode(payload), throwsFormatException); + }); + + test('rejects another chain, version 1, an empty network, a signer key, a missing key', () { + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'chain': 'solana'})), throwsFormatException); + expect( + () => NearSigningRequest.decode(envelope({...nearJson, 'v': 1})), + throwsA(isA()), + ); + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'network': ''})), throwsFormatException); + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'signer': signer})), throwsFormatException); + expect(() => NearSigningRequest.decode(envelope({...nearJson}..remove('network'))), throwsFormatException); + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'payload': '0x'})), throwsFormatException); + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'payload': 'abcd'})), throwsFormatException); + }); + }); } From 5cf864f617dd497f853695dae429b3561d90051e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:02:29 +0000 Subject: [PATCH 03/16] cold-wallet: review and sign NEAR transactions, export NEAR public keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scanner routes a v2 request to SignNearTransactionScreen, which finds the ML-DSA-65 account holding the key the transaction names, shows the network, signer, receiver and every action in full (key, code and account changes in warning colour), warns when account names do not belong to the labelled network, and on approval emits signature ‖ public key as the usual animated UR QR. ShowKeyScreen offers an ML-DSA-65 account's key in NEAR's form as an animated QR with its on-chain handle. The signature QR view is shared between the Quantus and NEAR screens. Debug builds get a NEAR sample catalogue built by a minimal borsh writer that matches near-cli-rs byte for byte. --- cold-wallet-app/analysis_options.yaml | 4 + .../lib/components/signature_qr_view.dart | 103 +++++ .../lib/debug/debug_calls_screen.dart | 49 +- .../lib/debug/debug_near_payloads.dart | 245 ++++++++++ .../lib/providers/wallet_providers.dart | 18 + .../lib/screens/near_key_export_screen.dart | 107 +++++ .../lib/screens/scan_transaction_screen.dart | 13 +- .../lib/screens/show_key_screen.dart | 47 ++ .../screens/sign_near_transaction_screen.dart | 422 ++++++++++++++++++ .../lib/screens/sign_transaction_screen.dart | 87 +--- .../lib/services/near_display.dart | 86 ++++ cold-wallet-app/test/near_display_test.dart | 106 +++++ .../sign_near_transaction_screen_test.dart | 180 ++++++++ 13 files changed, 1375 insertions(+), 92 deletions(-) create mode 100644 cold-wallet-app/lib/components/signature_qr_view.dart create mode 100644 cold-wallet-app/lib/debug/debug_near_payloads.dart create mode 100644 cold-wallet-app/lib/screens/near_key_export_screen.dart create mode 100644 cold-wallet-app/lib/screens/sign_near_transaction_screen.dart create mode 100644 cold-wallet-app/lib/services/near_display.dart create mode 100644 cold-wallet-app/test/near_display_test.dart create mode 100644 cold-wallet-app/test/sign_near_transaction_screen_test.dart diff --git a/cold-wallet-app/analysis_options.yaml b/cold-wallet-app/analysis_options.yaml index e82fe23fc..6ea1efb48 100644 --- a/cold-wallet-app/analysis_options.yaml +++ b/cold-wallet-app/analysis_options.yaml @@ -29,3 +29,7 @@ analyzer: missing_return: error must_be_immutable: error asset_does_not_exist: ignore + exclude: + - build/** + - android/** + - ios/** diff --git a/cold-wallet-app/lib/components/signature_qr_view.dart b/cold-wallet-app/lib/components/signature_qr_view.dart new file mode 100644 index 000000000..44a105cec --- /dev/null +++ b/cold-wallet-app/lib/components/signature_qr_view.dart @@ -0,0 +1,103 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; +import 'package:quantus_cold_wallet/providers/settings_providers.dart'; + +/// Shows [signed] as an animated UR QR for the hot wallet to scan, with the +/// frame-size and speed controls the user tuned in settings. +class SignatureQrView extends ConsumerStatefulWidget { + final Uint8List signed; + final String instruction; + + const SignatureQrView({super.key, required this.signed, required this.instruction}); + + @override + ConsumerState createState() => _SignatureQrViewState(); +} + +class _SignatureQrViewState extends ConsumerState { + List? _urParts; + int? _urPartsBytes; + bool _qrPaused = false; + + /// Pauses the animation and opens the tuning sheet; resumes when it closes. + Future _pauseAndTune() async { + setState(() => _qrPaused = true); + await BottomSheetContainer.show( + context, + builder: (ctx) => const BottomSheetContainer(title: 'QR display options', child: QrTuningControls()), + ); + if (mounted) setState(() => _qrPaused = false); + } + + @override + Widget build(BuildContext context) { + final colors = context.colorsV3; + final text = context.themeTextV3; + final settings = ref.watch(coldSettingsProvider); + + if (_urParts == null || _urPartsBytes != settings.qrBytes) { + _urParts = encodeUrForQr(data: widget.signed, maxFragmentLength: settings.qrBytes); + _urPartsBytes = settings.qrBytes; + } + final parts = _urParts!; + + return ScaffoldBase( + appBar: const V2AppBar(title: 'Signature', showBackButton: false), + mainContent: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 8), + Text( + widget.instruction, + style: text.body.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + Center( + child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: _qrPaused), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' + '${settings.qrBytes} bytes', + style: text.caption.copyWith(color: colors.textMuted), + ), + if (parts.length > 1) ...[ + const SizedBox(width: 12), + GestureDetector( + onTap: _pauseAndTune, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), + child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), + ), + ), + ], + ], + ), + if (parts.length > 1) ...[ + const SizedBox(height: 16), + Text( + 'Animated QR — keep both devices steady until the hot wallet finishes scanning.', + style: text.caption.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + ], + ], + ), + ), + bottomContent: ScaffoldBaseBottomContent( + child: QuantusButton.simple(label: 'Done', onTap: () => Navigator.popUntil(context, (r) => r.isFirst)), + ), + ); + } +} diff --git a/cold-wallet-app/lib/debug/debug_calls_screen.dart b/cold-wallet-app/lib/debug/debug_calls_screen.dart index 2e0655cc0..a189ca23d 100644 --- a/cold-wallet-app/lib/debug/debug_calls_screen.dart +++ b/cold-wallet-app/lib/debug/debug_calls_screen.dart @@ -1,8 +1,12 @@ +import 'dart:typed_data'; + import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/debug/debug_near_payloads.dart'; import 'package:quantus_cold_wallet/debug/debug_payloads.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; +import 'package:quantus_cold_wallet/screens/sign_near_transaction_screen.dart'; import 'package:quantus_cold_wallet/screens/sign_transaction_screen.dart'; /// Every call the signer can be asked to review, one tap from its review @@ -18,6 +22,7 @@ class DebugCallsScreen extends ConsumerWidget { final colors = context.colorsV3; final text = context.themeTextV3; final signer = ref.watch(addressProvider); + final nearSamples = DebugNearPayloads.samples(_nearSigningKey(ref)); return ScaffoldBase( appBar: const V2AppBar(title: 'Debug calls'), @@ -39,12 +44,26 @@ class DebugCallsScreen extends ConsumerWidget { for (final call in DebugPayloads.refused) _row(context, call, signer), _header(context, 'INVALID QR CODE DATA · ${DebugPayloads.invalidQrData.length}', colors.semanticEmber), for (final call in DebugPayloads.invalidQrData) _row(context, call, signer), + _header(context, 'NEAR · ${nearSamples.length}', colors.accentFlare), + for (final call in nearSamples) _nearRow(context, call), const SizedBox(height: 24), ], ), ); } + /// The raw public key of the wallet's first ML-DSA-65 account, the only kind + /// of key a NEAR transaction can name here. Null when there is none. + Uint8List? _nearSigningKey(WidgetRef ref) { + final address = ref + .watch(addressesProvider) + .entries + .where((e) => e.value.scheme == DilithiumScheme.mlDsa65) + .firstOrNull + ?.key; + return address == null ? null : keypairFor(ref, address)?.publicKey; + } + Widget _header(BuildContext context, String title, Color color) { return Padding( padding: const EdgeInsets.only(top: 20, bottom: 8), @@ -52,20 +71,30 @@ class DebugCallsScreen extends ConsumerWidget { ); } - Widget _row(BuildContext context, DebugCall call, String signer) { + Widget _row(BuildContext context, DebugCall call, String signer) => _tile( + context, + call.label, + () => SignTransactionScreen( + request: SigningRequest(signer: call.signer ?? signer, payload: DebugPayloads.payloadForCall(call.call)), + ), + ); + + Widget _nearRow(BuildContext context, DebugNearCall call) => _tile( + context, + call.label, + () => SignNearTransactionScreen( + request: call.request, + transaction: decodeNearTransaction(transaction: call.request.transaction), + ), + ); + + Widget _tile(BuildContext context, String label, Widget Function() screen) { final colors = context.colorsV3; final text = context.themeTextV3; return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => SignTransactionScreen( - request: SigningRequest(signer: call.signer ?? signer, payload: DebugPayloads.payloadForCall(call.call)), - ), - ), - ), + onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => screen())), child: Container( padding: const EdgeInsets.symmetric(vertical: 14), decoration: BoxDecoration( @@ -74,7 +103,7 @@ class DebugCallsScreen extends ConsumerWidget { child: Row( children: [ Expanded( - child: Text(call.label, style: text.body.copyWith(color: colors.textContent)), + child: Text(label, style: text.body.copyWith(color: colors.textContent)), ), Icon(Icons.chevron_right, size: 18, color: colors.textMuted), ], diff --git a/cold-wallet-app/lib/debug/debug_near_payloads.dart b/cold-wallet-app/lib/debug/debug_near_payloads.dart new file mode 100644 index 000000000..62c2ad400 --- /dev/null +++ b/cold-wallet-app/lib/debug/debug_near_payloads.dart @@ -0,0 +1,245 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:quantus_sdk/quantus_sdk.dart'; + +/// One NEAR transaction the signer can be asked to review. +class DebugNearCall { + final String label; + final NearSigningRequest request; + + const DebugNearCall({required this.label, required this.request}); +} + +/// Sample NEAR signing requests for the simulator, built with a minimal borsh +/// writer so they are byte-identical in shape to a hot wallet's `TransactionV0`. +/// +/// [signingKey] is the raw 1952-byte ML-DSA-65 public key the wallet's own +/// account holds, so the review screen finds a signer; null when the wallet +/// has no ML-DSA-65 account, in which case every sample is refused as a key +/// not held here, which is also worth seeing. +class DebugNearPayloads { + const DebugNearPayloads._(); + + static final BigInt _near = BigInt.from(10).pow(24); + static final BigInt _tgas = BigInt.from(10).pow(12); + static final Uint8List _blockHash = Uint8List.fromList(List.filled(32, 0x22)); + static final Uint8List _ed25519Key = Uint8List.fromList(List.filled(32, 0x11)); + static final Uint8List _foreignMlDsaKey = Uint8List.fromList(List.filled(1952, 0x42)); + + static List samples(Uint8List? signingKey) { + final key = signingKey ?? _foreignMlDsaKey; + return [ + DebugNearCall( + label: 'send 1.5 NEAR', + request: _testnet( + _tx(key, receiver: 'bob.testnet', actions: [_transfer(_near * BigInt.from(15) ~/ BigInt.from(10))]), + ), + ), + DebugNearCall( + label: 'ft_transfer on a token contract', + request: _testnet( + _tx( + key, + receiver: 'token.rhea.testnet', + actions: [ + _functionCall( + 'ft_transfer', + args: {'receiver_id': 'bob.testnet', 'amount': '1000000', 'memo': null}, + gas: _tgas * BigInt.from(30), + deposit: BigInt.one, + ), + ], + ), + ), + ), + DebugNearCall( + label: 'swap: ft_transfer_call then storage_deposit [two actions]', + request: _testnet( + _tx( + key, + receiver: 'ref-finance.testnet', + actions: [ + _functionCall( + 'storage_deposit', + args: {}, + gas: _tgas * BigInt.from(30), + deposit: _near ~/ BigInt.from(80), + ), + _functionCall( + 'ft_transfer_call', + args: {'receiver_id': 'ref-finance.testnet', 'amount': '500000', 'msg': '{"actions":[]}'}, + gas: _tgas * BigInt.from(180), + deposit: BigInt.one, + ), + ], + ), + ), + ), + DebugNearCall( + label: 'add a full access key [dangerous]', + request: _testnet(_tx(key, receiver: 'alice.testnet', actions: [_addFullAccessKey(_ed25519Key)])), + ), + DebugNearCall( + label: 'add a function call key limited to one contract', + request: _testnet( + _tx( + key, + receiver: 'alice.testnet', + actions: [ + _addFunctionCallKey( + _ed25519Key, + receiver: 'app.rhea.testnet', + methods: ['claim', 'stake'], + allowance: _near ~/ BigInt.from(4), + ), + ], + ), + ), + ), + DebugNearCall( + label: 'delete the account, balance to bob [dangerous]', + request: _testnet(_tx(key, receiver: 'alice.testnet', actions: [_deleteAccount('bob.testnet')])), + ), + DebugNearCall( + label: 'mainnet label on testnet account names [mismatch]', + request: NearSigningRequest( + network: 'mainnet', + transaction: _tx(key, receiver: 'bob.testnet', actions: [_transfer(_near)]), + ), + ), + DebugNearCall( + label: 'signed by an ed25519 key [refused]', + request: _testnet(_tx(_ed25519Key, receiver: 'bob.testnet', actions: [_transfer(_near)])), + ), + DebugNearCall( + label: 'signed by an ML-DSA-65 key this wallet does not hold [refused]', + request: _testnet(_tx(_foreignMlDsaKey, receiver: 'bob.testnet', actions: [_transfer(_near)])), + ), + ]; + } + + static NearSigningRequest _testnet(Uint8List tx) => NearSigningRequest(network: 'testnet', transaction: tx); + + /// borsh `TransactionV0`: signer, public key, nonce, receiver, block hash, actions. + static Uint8List _tx(Uint8List publicKey, {required String receiver, required List> actions}) { + final w = _Borsh() + ..string('alice.testnet') + ..publicKey(publicKey) + ..u64(BigInt.from(42)) + ..string(receiver) + ..bytes(_blockHash) + ..u32(actions.length); + for (final action in actions) { + w.bytes(action); + } + return w.take(); + } + + static List _transfer(BigInt deposit) => + (_Borsh() + ..u8(3) + ..u128(deposit)) + .take(); + + static List _functionCall( + String method, { + required Map args, + required BigInt gas, + required BigInt deposit, + }) { + final encodedArgs = utf8.encode(jsonEncode(args)); + return (_Borsh() + ..u8(2) + ..string(method) + ..u32(encodedArgs.length) + ..bytes(encodedArgs) + ..u64(gas) + ..u128(deposit)) + .take(); + } + + static List _addFullAccessKey(Uint8List key) => + (_Borsh() + ..u8(5) + ..publicKey(key) + ..u64(BigInt.zero) // access key nonce + ..u8(1)) // FullAccess + .take(); + + static List _addFunctionCallKey( + Uint8List key, { + required String receiver, + required List methods, + BigInt? allowance, + }) { + final w = _Borsh() + ..u8(5) + ..publicKey(key) + ..u64(BigInt.zero) + ..u8(0); // FunctionCall permission + if (allowance == null) { + w.u8(0); + } else { + w + ..u8(1) + ..u128(allowance); + } + w + ..string(receiver) + ..u32(methods.length); + for (final method in methods) { + w.string(method); + } + return w.take(); + } + + static List _deleteAccount(String beneficiary) => + (_Borsh() + ..u8(7) + ..string(beneficiary)) + .take(); +} + +/// Just enough borsh to write a `TransactionV0`. +class _Borsh { + final BytesBuilder _out = BytesBuilder(); + + void u8(int v) => _out.addByte(v); + + void u32(int v) => _out.add((ByteData(4)..setUint32(0, v, Endian.little)).buffer.asUint8List()); + + void u64(BigInt v) => _le(v, 8); + + void u128(BigInt v) => _le(v, 16); + + void bytes(List v) => _out.add(v); + + void string(String s) { + final encoded = utf8.encode(s); + u32(encoded.length); + bytes(encoded); + } + + /// A NEAR `PublicKey`: curve tag then the raw key. 32 bytes is ed25519 (0), + /// 1952 is ML-DSA-65 (2). + void publicKey(Uint8List key) { + u8(switch (key.length) { + 32 => 0, + 1952 => 2, + _ => throw ArgumentError('Not an ed25519 or ML-DSA-65 key: ${key.length} bytes'), + }); + bytes(key); + } + + void _le(BigInt v, int width) { + var rest = v; + final mask = BigInt.from(0xff); + for (var i = 0; i < width; i++) { + u8((rest & mask).toInt()); + rest >>= 8; + } + } + + Uint8List take() => _out.takeBytes(); +} diff --git a/cold-wallet-app/lib/providers/wallet_providers.dart b/cold-wallet-app/lib/providers/wallet_providers.dart index c07c0a21c..a84cf5942 100644 --- a/cold-wallet-app/lib/providers/wallet_providers.dart +++ b/cold-wallet-app/lib/providers/wallet_providers.dart @@ -1,3 +1,4 @@ +import 'package:convert/convert.dart'; import 'package:cryptography/cryptography.dart' show SecretBoxAuthenticationError; import 'package:flutter/foundation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -241,6 +242,23 @@ Keypair? keypairFor(WidgetRef ref, String address) { return HdWalletService().keyPairAtPath(mnemonic, account.derivationPath, account.scheme); } +/// The address of the account whose ML-DSA-65 public key is [publicKeyHex], +/// or null when no account in this wallet holds it. A NEAR transaction names +/// its key rather than a Quantus address, and the address is a hash of the +/// key, so each ML-DSA-65 account's key pair is derived and compared. Keyed +/// by hex so equal keys resolve to the same provider instance. +final nearKeyOwnerProvider = Provider.family((ref, publicKeyHex) { + final mnemonic = ref.watch(walletControllerProvider).mnemonic; + if (mnemonic == null) return null; + final service = HdWalletService(); + for (final entry in ref.watch(addressesProvider).entries) { + if (entry.value.scheme != DilithiumScheme.mlDsa65) continue; + final keypair = service.keyPairAtPath(mnemonic, entry.value.derivationPath, entry.value.scheme); + if (hex.encode(keypair.publicKey) == publicKeyHex) return entry.key; + } + return null; +}); + /// The first account, which the home screen leads with. final addressProvider = Provider((ref) => ref.watch(addressesProvider).keys.firstOrNull); diff --git a/cold-wallet-app/lib/screens/near_key_export_screen.dart b/cold-wallet-app/lib/screens/near_key_export_screen.dart new file mode 100644 index 000000000..4df735090 --- /dev/null +++ b/cold-wallet-app/lib/screens/near_key_export_screen.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; +import 'package:quantus_cold_wallet/providers/settings_providers.dart'; + +/// Shows one account's ML-DSA-65 key in NEAR's form, as an animated UR QR +/// for a hot wallet or CLI to scan and as text for anyone copying it by hand. +/// +/// The key is 1952 bytes, well past a single QR, hence the animation. The +/// `ml-dsa-65-hash:` handle under it is what NEAR lists on chain once the key +/// is added, so a user can confirm there that the key they added is this one. +class NearKeyExportScreen extends ConsumerStatefulWidget { + final NearPublicKeyExport export; + final String handle; + + const NearKeyExportScreen({super.key, required this.export, required this.handle}); + + @override + ConsumerState createState() => _NearKeyExportScreenState(); +} + +class _NearKeyExportScreenState extends ConsumerState { + List? _urParts; + int? _urPartsBytes; + bool _qrPaused = false; + + Future _pauseAndTune() async { + setState(() => _qrPaused = true); + await BottomSheetContainer.show( + context, + builder: (ctx) => const BottomSheetContainer(title: 'QR display options', child: QrTuningControls()), + ); + if (mounted) setState(() => _qrPaused = false); + } + + @override + Widget build(BuildContext context) { + final colors = context.colorsV3; + final text = context.themeTextV3; + final settings = ref.watch(coldSettingsProvider); + + if (_urParts == null || _urPartsBytes != settings.qrBytes) { + _urParts = encodeUrForQr(data: widget.export.encode(), maxFragmentLength: settings.qrBytes); + _urPartsBytes = settings.qrBytes; + } + final parts = _urParts!; + + return ScaffoldBase( + appBar: const V2AppBar(title: 'NEAR Public Key'), + mainContent: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 8), + Text( + 'Scan with the device that prepares your NEAR transactions. Add this key to your NEAR account as a ' + 'full access key, then transactions for that account can be signed here.', + style: text.body.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + Center( + child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: _qrPaused), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' + '${settings.qrBytes} bytes', + style: text.caption.copyWith(color: colors.textMuted), + ), + if (parts.length > 1) ...[ + const SizedBox(width: 12), + GestureDetector( + onTap: _pauseAndTune, + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), + child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), + ), + ), + ], + ], + ), + const SizedBox(height: 24), + DetailSummaryRow.stacked( + label: 'On-chain handle', + value: widget.handle, + monospace: true, + note: 'How NEAR lists this key once it is added. Check it matches on the explorer.', + ), + DetailSummaryRow.stacked(label: 'Quantus account', value: widget.export.address, monospace: true), + DetailSummaryRow.stacked(label: 'Full key', value: widget.export.nearPublicKey, monospace: true), + const SizedBox(height: 24), + ], + ), + ), + bottomContent: ScaffoldBaseBottomContent( + child: QuantusButton.simple(label: 'Done', onTap: () => Navigator.pop(context)), + ), + ); + } +} diff --git a/cold-wallet-app/lib/screens/scan_transaction_screen.dart b/cold-wallet-app/lib/screens/scan_transaction_screen.dart index 37ad83cfc..9e764ab79 100644 --- a/cold-wallet-app/lib/screens/scan_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/scan_transaction_screen.dart @@ -4,6 +4,7 @@ import 'package:mobile_scanner/mobile_scanner.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:quantus_cold_wallet/app_version.dart'; import 'package:quantus_cold_wallet/debug/debug_calls_screen.dart'; +import 'package:quantus_cold_wallet/screens/sign_near_transaction_screen.dart'; import 'package:quantus_cold_wallet/screens/sign_transaction_screen.dart'; /// Scans a (possibly multi-part / animated) UR QR code, accumulating parts @@ -80,10 +81,16 @@ class _ScanTransactionScreenState extends State { _done = true; try { - final request = SigningRequest.decode(decodeUr(urParts: parts)); + final screen = switch (AnySigningRequest.decode(decodeUr(urParts: parts))) { + final SigningRequest request => SignTransactionScreen(request: request), + final NearSigningRequest request => SignNearTransactionScreen( + request: request, + transaction: decodeNearTransaction(transaction: request.transaction), + ), + }; _controller.stop(); if (!mounted) return; - Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => SignTransactionScreen(request: request))); + Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => screen)); } catch (e) { debugPrint('Rejected signing request QR: $e'); // Keeping the parts would make every replayed frame a duplicate, so the @@ -92,6 +99,8 @@ class _ScanTransactionScreenState extends State { _restartAccumulation(switch (e) { UnsupportedSigningRequestVersionException() => '${e.message}. $updateAppHint $currentAppVersion', FormatException() => 'Not a Quantus signing request: ${e.message}', + // decodeNearTransaction reports a malformed transaction as a bare string. + String() => 'Could not read the NEAR transaction: $e', _ => 'Failed to decode QR: $e', }); } diff --git a/cold-wallet-app/lib/screens/show_key_screen.dart b/cold-wallet-app/lib/screens/show_key_screen.dart index 6a263f5db..6dfa41b52 100644 --- a/cold-wallet-app/lib/screens/show_key_screen.dart +++ b/cold-wallet-app/lib/screens/show_key_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; +import 'package:quantus_cold_wallet/screens/near_key_export_screen.dart'; class ShowKeyScreen extends ConsumerWidget { /// The account to show; defaults to the wallet's first. @@ -15,6 +16,7 @@ class ShowKeyScreen extends ConsumerWidget { final text = context.themeTextV3; final address = this.address ?? ref.watch(addressProvider); final checkphrase = address == null ? const AsyncValue.data('') : ref.watch(checksumNameProvider(address)); + final account = address == null ? null : ref.watch(addressesProvider)[address]; return ScaffoldBase( appBar: const V2AppBar(title: 'Show Key'), @@ -53,9 +55,54 @@ class ShowKeyScreen extends ConsumerWidget { error: (_, _) => const SizedBox.shrink(), ), const SizedBox(height: 24), + if (account?.scheme == DilithiumScheme.mlDsa65) _nearSection(context, ref, address), ], ), ), ); } + + /// ML-DSA-65 is the one scheme NEAR accepts, so only those accounts offer + /// their key in NEAR's form. The key pair is derived on tap, not on build. + Widget _nearSection(BuildContext context, WidgetRef ref, String address) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration(color: colors.bgSurface, borderRadius: context.radiusV3.mdBorder), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text('NEAR', style: text.labelMonogram.copyWith(color: colors.accentFlare, letterSpacing: 1.2)), + const SizedBox(height: 8), + Text( + 'This key can also hold a NEAR account. Export it in NEAR\'s form to add it there; NEAR transactions ' + 'for that account are then reviewed and signed on this device.', + style: text.caption.copyWith(color: colors.textMuted), + ), + const SizedBox(height: 16), + QuantusButton.simple( + label: 'Show NEAR public key', + variant: ButtonVariant.staged, + onTap: () { + final keypair = keypairFor(ref, address); + if (keypair == null) return; + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => NearKeyExportScreen( + export: NearPublicKeyExport( + address: address, + nearPublicKey: nearPublicKeyText(keypair: keypair), + ), + handle: nearPublicKeyHandle(keypair: keypair), + ), + ), + ); + }, + ), + ], + ), + ); + } } diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart new file mode 100644 index 000000000..1b70d6a42 --- /dev/null +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -0,0 +1,422 @@ +import 'dart:typed_data'; + +import 'package:convert/convert.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/components/address_with_checkphrase.dart'; +import 'package:quantus_cold_wallet/components/signature_qr_view.dart'; +import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; +import 'package:quantus_cold_wallet/services/near_display.dart'; + +/// Reviews a NEAR transaction a hot wallet asked this device to sign, and on +/// approval produces the `signature ‖ public key` QR. +/// +/// A NEAR transaction names the key that must sign it rather than a Quantus +/// address, so the screen first finds which account here holds that key. The +/// receiver and every action are listed in full: a NEAR transaction can carry +/// several, and the dangerous ones — key and account changes, code +/// deployment — are marked so they are not mistaken for a plain send. +class SignNearTransactionScreen extends ConsumerStatefulWidget { + final NearSigningRequest request; + + /// [request]'s transaction, decoded by whoever scanned it; undecodable + /// bytes never reach this screen. + final NearTransaction transaction; + + const SignNearTransactionScreen({super.key, required this.request, required this.transaction}); + + @override + ConsumerState createState() => _SignNearTransactionScreenState(); +} + +class _SignNearTransactionScreenState extends ConsumerState { + Uint8List? _signed; + bool _signing = false; + String? _error; + + NearTransaction get tx => widget.transaction; + + void _sign(String owner) { + final keypair = keypairFor(ref, owner); + if (keypair == null) { + setState(() => _error = 'Wallet is locked — unlock and try again. Nothing was signed.'); + return; + } + setState(() { + _signing = true; + _error = null; + }); + try { + // Pure ML-DSA-65 over SHA-256 of the transaction, no Quantus context: + // this is what the NEAR runtime verifies. Returns signature ++ public key. + final signed = keypair.signNear(widget.request.transaction); + setState(() { + _signing = false; + _signed = signed; + }); + } catch (e) { + setState(() { + _signing = false; + _error = 'Signing failed: $e'; + }); + } + } + + @override + Widget build(BuildContext context) { + final muted = context.themeTextV3.body.copyWith(color: context.colorsV3.textMuted); + if (!tx.signsWithMlDsa65) { + return _errorView( + context, + title: 'Not a Quantus key', + message: + 'This transaction names a key of type ${_keyKind(tx.publicKey)}. This wallet holds only ML-DSA-65 ' + 'keys, so it cannot sign it.', + detail: Text(tx.publicKey, style: muted, textAlign: TextAlign.center), + ); + } + if (ref.watch(walletControllerProvider).status != WalletStatus.unlocked) { + return _errorView( + context, + title: 'Wallet is locked', + message: 'Unlock the wallet and scan the request again.', + detail: Text(tx.publicKey, style: muted, textAlign: TextAlign.center), + ); + } + final owner = ref.watch(nearKeyOwnerProvider(hex.encode(tx.publicKeyBytes))); + if (owner == null) { + return _errorView( + context, + title: 'Key not in this wallet', + message: 'No ML-DSA-65 account in this cold wallet holds the key this transaction names.', + detail: DetailSummaryRow.stacked(label: 'Requested key', value: tx.publicKey, monospace: true), + ); + } + if (_signed != null) { + return SignatureQrView( + signed: _signed!, + instruction: 'Scan this with your hot wallet to broadcast the NEAR transaction.', + ); + } + return _reviewView(context, owner); + } + + static String _keyKind(String publicKey) { + final colon = publicKey.indexOf(':'); + return colon < 0 ? 'different' : publicKey.substring(0, colon); + } + + Widget _errorView(BuildContext context, {required String title, required String message, required Widget detail}) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return ScaffoldBase( + appBar: const V2AppBar(title: 'Sign NEAR Transaction'), + mainContent: Center( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration(color: colors.semanticEmber.useOpacity(0.12), shape: BoxShape.circle), + child: Icon(Icons.error_outline, size: 72, color: colors.semanticEmber), + ), + ), + const SizedBox(height: 24), + Text( + title, + style: text.titleHero.copyWith(color: colors.semanticEmber), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + Text( + message, + style: text.bodyLarge.copyWith(color: colors.textContent), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colors.semanticEmber.useOpacity(0.08), + borderRadius: context.radiusV3.mdBorder, + border: Border.all(color: colors.semanticEmber), + ), + child: detail, + ), + const SizedBox(height: 20), + Text( + 'Nothing was signed.', + style: text.bodyEmphasis.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + bottomContent: ScaffoldBaseBottomContent( + child: QuantusButton.simple(label: 'Back to home', onTap: () => Navigator.popUntil(context, (r) => r.isFirst)), + ), + ); + } + + Widget _reviewView(BuildContext context, String owner) { + final colors = context.colorsV3; + final text = context.themeTextV3; + final dangerous = tx.actions.any(NearDisplay.isDangerous); + final mismatch = NearDisplay.networkMismatch(tx, widget.request.network); + final soleTransfer = tx.actions.length == 1 && tx.actions.single.kind == NearActionKind.transfer; + + return ScaffoldBase( + appBar: const V2AppBar(title: 'Review & Sign'), + mainContent: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 8), + _networkChip(context), + if (mismatch != null) _warningBanner(context, title: 'Network does not match', body: mismatch), + if (dangerous) + _warningBanner( + context, + title: 'This changes control of ${tx.signerId}', + body: + 'Keys, code or the account itself are being changed, not just funds moved. Only sign this if ' + 'you asked for exactly these actions.', + ), + const SizedBox(height: 8), + Text( + NearDisplay.headline(tx), + style: text.titleScreen.copyWith(color: dangerous ? colors.semanticEmber : colors.accentFlare), + ), + if (soleTransfer) ...[ + _amountHero(context, tx.actions.single.amount ?? BigInt.zero), + DetailSummaryRow.stacked(label: 'To', value: tx.receiverId, monospace: true), + DetailSummaryRow.stacked(label: 'From', value: tx.signerId, monospace: true), + ] else ...[ + DetailSummaryRow.stacked(label: 'Receiver', value: tx.receiverId, monospace: true), + DetailSummaryRow.stacked(label: 'Signed by', value: tx.signerId, monospace: true), + for (final (i, action) in tx.actions.indexed) ..._actionBody(context, action, i), + ], + AddressWithCheckphrase(label: 'Signing key held by', address: owner), + const SizedBox(height: 20), + _advancedSection(context), + if (_error != null) ...[ + const SizedBox(height: 16), + Text( + _error!, + style: text.caption.copyWith(color: colors.semanticEmber), + textAlign: TextAlign.center, + ), + ], + const SizedBox(height: 8), + ], + ), + ), + bottomContent: ScaffoldBaseBottomContent( + child: Row( + children: [ + Expanded( + child: QuantusButton.simple( + label: 'Cancel', + variant: ButtonVariant.staged, + onTap: () => Navigator.popUntil(context, (r) => r.isFirst), + ), + ), + const SizedBox(width: 16), + Expanded( + child: QuantusButton.simple( + label: 'Sign', + isLoading: _signing, + onTap: _signing ? null : () => _sign(owner), + ), + ), + ], + ), + ), + ); + } + + Widget _networkChip(BuildContext context) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return Align( + alignment: Alignment.centerLeft, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration(color: colors.bgSurface2, borderRadius: context.radiusV3.xsBorder), + child: Text( + 'NEAR · ${widget.request.network.toUpperCase()}', + style: text.labelMonogram.copyWith(color: colors.textContent, letterSpacing: 1.2), + ), + ), + ); + } + + Widget _warningBanner(BuildContext context, {required String title, required String body}) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return Container( + margin: const EdgeInsets.only(top: 16), + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: colors.semanticEmber.useOpacity(0.12), + borderRadius: context.radiusV3.mdBorder, + border: Border.all(color: colors.semanticEmber), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(Icons.warning_amber_rounded, color: colors.semanticEmber, size: 22), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: text.bodyEmphasis.copyWith(color: colors.semanticEmber)), + const SizedBox(height: 4), + Text(body, style: text.caption.copyWith(color: colors.textMuted)), + ], + ), + ), + ], + ), + ); + } + + Widget _amountHero(BuildContext context, BigInt yocto) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: Alignment.centerLeft, + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: NearDisplay.formatNear(yocto), + style: text.amountHero.copyWith(color: colors.textContent), + ), + TextSpan( + text: ' NEAR', + style: text.amountInline.copyWith(color: colors.textMuted), + ), + ], + ), + ), + ), + ); + } + + /// Every parameter of one action, under a heading that names it. Nothing is + /// summarised away: a function-call key's method list or a stake's validator + /// key is exactly what a signer has to check. + List _actionBody(BuildContext context, NearAction action, int index) { + final colors = context.colorsV3; + final text = context.themeTextV3; + final danger = NearDisplay.isDangerous(action); + final heading = Padding( + padding: const EdgeInsets.only(top: 16), + child: Text( + '${index + 1}. ${NearDisplay.actionTitle(action)}', + style: text.headingRow.copyWith(color: danger ? colors.semanticEmber : colors.semanticLilac), + ), + ); + String near(BigInt? yocto) => '${NearDisplay.formatNear(yocto ?? BigInt.zero)} NEAR'; + + final rows = switch (action.kind) { + NearActionKind.transfer => [DetailSummaryRow.stacked(label: 'Amount', value: near(action.amount))], + NearActionKind.functionCall => [ + DetailSummaryRow.stacked(label: 'Method', value: action.target ?? '', monospace: true), + DetailSummaryRow.stacked(label: 'Attached deposit', value: near(action.amount)), + DetailSummaryRow.stacked(label: 'Gas', value: NearDisplay.formatGas(action.gas ?? BigInt.zero)), + DetailSummaryRow.stacked( + label: 'Arguments', + value: NearDisplay.argsText(action.args ?? Uint8List(0)), + monospace: true, + ), + ], + NearActionKind.createAccount => [ + DetailSummaryRow.stacked(label: 'New account', value: tx.receiverId, monospace: true), + ], + NearActionKind.deployContract => [ + DetailSummaryRow.stacked(label: 'Deploys to', value: tx.receiverId, monospace: true), + DetailSummaryRow.stacked( + label: 'Code size', + value: '${action.codeLen ?? 0} bytes', + note: 'The code itself cannot be reviewed here.', + ), + ], + NearActionKind.stake => [ + DetailSummaryRow.stacked(label: 'Amount', value: near(action.amount)), + DetailSummaryRow.stacked(label: 'Validator key', value: action.publicKey ?? '', monospace: true), + ], + NearActionKind.addKey => [ + DetailSummaryRow.stacked(label: 'Key', value: action.publicKey ?? '', monospace: true), + if (action.fullAccess) + DetailSummaryRow.stacked( + label: 'Access', + value: 'Full access', + valueColor: colors.semanticEmber, + note: 'This key can do anything the account can, including removing your key.', + ) + else ...[ + DetailSummaryRow.stacked(label: 'May call', value: action.target ?? '', monospace: true), + DetailSummaryRow.stacked( + label: 'Methods', + value: action.methodNames.isEmpty ? 'Any method' : action.methodNames.join(', '), + monospace: action.methodNames.isNotEmpty, + ), + DetailSummaryRow.stacked( + label: 'Gas allowance', + value: action.amount == null ? 'Unlimited' : near(action.amount), + ), + ], + ], + NearActionKind.deleteKey => [ + DetailSummaryRow.stacked(label: 'Key', value: action.publicKey ?? '', monospace: true), + ], + NearActionKind.deleteAccount => [ + DetailSummaryRow.stacked(label: 'Deletes', value: tx.receiverId, monospace: true), + DetailSummaryRow.stacked(label: 'Balance goes to', value: action.target ?? '', monospace: true), + ], + }; + return [heading, ...rows]; + } + + Widget _advancedSection(BuildContext context) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => BottomSheetContainer.show( + context, + builder: (ctx) => BottomSheetContainer( + title: 'Advanced', + child: Text(_advancedLines().join('\n'), style: text.dataAddress.copyWith(color: colors.textMuted)), + ), + ), + child: Row( + children: [ + Text('ADVANCED', style: text.labelData.copyWith(color: colors.textMuted)), + const SizedBox(width: 6), + Icon(Icons.chevron_right, size: 18, color: colors.textMuted), + ], + ), + ); + } + + List _advancedLines() => [ + 'Network: ${widget.request.network}', + 'Nonce: ${tx.nonce}', + 'Block hash: ${tx.blockHash}', + 'Public key: ${tx.publicKey}', + 'Transaction hash: ${hex.encode(tx.hash)}', + 'Raw transaction (${widget.request.transaction.length} bytes): 0x${hex.encode(widget.request.transaction)}', + ]; +} diff --git a/cold-wallet-app/lib/screens/sign_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_transaction_screen.dart index 4f0409aeb..afc84bda4 100644 --- a/cold-wallet-app/lib/screens/sign_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_transaction_screen.dart @@ -7,8 +7,7 @@ import 'package:quantus_sdk/quantus_sdk.dart' hide CallFieldView; import 'package:quantus_cold_wallet/app_version.dart'; import 'package:quantus_cold_wallet/components/address_with_checkphrase.dart'; import 'package:quantus_cold_wallet/components/call_detail_view.dart'; -import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; -import 'package:quantus_cold_wallet/providers/settings_providers.dart'; +import 'package:quantus_cold_wallet/components/signature_qr_view.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; /// Reviews a scanned signing payload and, on approval, produces the signature QR. @@ -30,9 +29,6 @@ class _SignTransactionScreenState extends ConsumerState { ParsedPayload? _parsed; FormatException? _parseError; Uint8List? _signed; - List? _urParts; - int? _urPartsBytes; - bool _qrPaused = false; bool _signing = false; String? _error; @@ -98,7 +94,12 @@ class _SignTransactionScreenState extends ConsumerState { detail: AddressWithCheckphrase(label: 'Requested signer', address: widget.request.signer), ); } - if (_signed != null) return _signatureView(context, _signed!); + if (_signed != null) { + return SignatureQrView( + signed: _signed!, + instruction: 'Scan this with your hot wallet to broadcast the transaction.', + ); + } return _reviewView(context, _parsed!); } @@ -347,78 +348,4 @@ class _SignTransactionScreenState extends ConsumerState { builder: (ctx) => BottomSheetContainer(title: title, child: child), ); } - - /// Pauses the animation and opens the tuning sheet; resumes when it closes. - Future _pauseAndTune() async { - setState(() => _qrPaused = true); - await _showSheet(title: 'QR display options', child: const QrTuningControls()); - if (mounted) setState(() => _qrPaused = false); - } - - Widget _signatureView(BuildContext context, Uint8List signed) { - final colors = context.colorsV3; - final text = context.themeTextV3; - final settings = ref.watch(coldSettingsProvider); - - if (_urParts == null || _urPartsBytes != settings.qrBytes) { - _urParts = encodeUrForQr(data: signed, maxFragmentLength: settings.qrBytes); - _urPartsBytes = settings.qrBytes; - } - final parts = _urParts!; - - return ScaffoldBase( - appBar: const V2AppBar(title: 'Signature', showBackButton: false), - mainContent: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 8), - Text( - 'Scan this with your hot wallet to broadcast the transaction.', - style: text.body.copyWith(color: colors.textMuted), - textAlign: TextAlign.center, - ), - const SizedBox(height: 24), - Center( - child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: _qrPaused), - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' - '${settings.qrBytes} bytes', - style: text.caption.copyWith(color: colors.textMuted), - ), - if (parts.length > 1) ...[ - const SizedBox(width: 12), - GestureDetector( - onTap: _pauseAndTune, - child: Container( - width: 32, - height: 32, - decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), - child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), - ), - ), - ], - ], - ), - if (parts.length > 1) ...[ - const SizedBox(height: 16), - Text( - 'Animated QR — keep both devices steady until the hot wallet finishes scanning.', - style: text.caption.copyWith(color: colors.textMuted), - textAlign: TextAlign.center, - ), - ], - ], - ), - ), - bottomContent: ScaffoldBaseBottomContent( - child: QuantusButton.simple(label: 'Done', onTap: () => Navigator.popUntil(context, (r) => r.isFirst)), - ), - ); - } } diff --git a/cold-wallet-app/lib/services/near_display.dart b/cold-wallet-app/lib/services/near_display.dart new file mode 100644 index 000000000..fd1f01ff1 --- /dev/null +++ b/cold-wallet-app/lib/services/near_display.dart @@ -0,0 +1,86 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:convert/convert.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +/// Display rules for reviewing a NEAR transaction. Pure functions over the +/// decoded [NearTransaction], so they can be checked without the Rust bridge. +class NearDisplay { + const NearDisplay._(); + + static const nearDecimals = 24; + static final BigInt _yoctoPerNear = BigInt.from(10).pow(nearDecimals); + static final BigInt _gasPerTgas = BigInt.from(10).pow(12); + + /// Exact: every significant decimal is kept, so a signer never sees a + /// rounded figure for what they are authorising. + static String formatNear(BigInt yocto) { + final whole = yocto ~/ _yoctoPerNear; + var fraction = (yocto % _yoctoPerNear).toString().padLeft(nearDecimals, '0'); + fraction = fraction.replaceFirst(RegExp(r'0+$'), ''); + return fraction.isEmpty ? '$whole' : '$whole.$fraction'; + } + + static String formatGas(BigInt gas) { + if (gas % _gasPerTgas == BigInt.zero) return '${gas ~/ _gasPerTgas} Tgas'; + return '$gas gas'; + } + + /// Function-call arguments are JSON by convention; anything else is shown as hex. + static String argsText(Uint8List args) { + if (args.isEmpty) return '(none)'; + try { + final decoded = json.decode(utf8.decode(args, allowMalformed: false)); + return const JsonEncoder.withIndent(' ').convert(decoded); + } on FormatException { + return '0x${hex.encode(args)}'; + } + } + + static String actionTitle(NearAction action) => switch (action.kind) { + NearActionKind.transfer => 'SEND', + NearActionKind.functionCall => 'CALL CONTRACT', + NearActionKind.createAccount => 'CREATE ACCOUNT', + NearActionKind.deployContract => 'DEPLOY CONTRACT', + NearActionKind.stake => 'STAKE', + NearActionKind.addKey => action.fullAccess ? 'ADD FULL ACCESS KEY' : 'ADD FUNCTION CALL KEY', + NearActionKind.deleteKey => 'DELETE KEY', + NearActionKind.deleteAccount => 'DELETE ACCOUNT', + }; + + /// Actions that change who controls the account or what code runs under it, + /// rather than moving a stated amount. Shown in warning colour. + static bool isDangerous(NearAction action) => switch (action.kind) { + NearActionKind.addKey || NearActionKind.deleteKey || NearActionKind.deleteAccount => true, + NearActionKind.deployContract || NearActionKind.stake => true, + NearActionKind.transfer || NearActionKind.functionCall || NearActionKind.createAccount => false, + }; + + /// What the whole transaction does, in one line: the single action's title, + /// or the count when there are several. + static String headline(NearTransaction tx) { + if (tx.actions.length == 1) return actionTitle(tx.actions.single); + return '${tx.actions.length} ACTIONS'; + } + + /// The envelope's network is only a label the hot wallet attached; the + /// account names are what the chain will see. Named accounts end in the + /// network's top-level name, so a mismatch means one side is wrong. + static String? networkMismatch(NearTransaction tx, String network) { + final tld = switch (network) { + 'mainnet' => '.near', + 'testnet' => '.testnet', + _ => null, + }; + if (tld == null) return null; + final mismatched = [tx.signerId, tx.receiverId].where((id) => _isNamed(id) && !id.endsWith(tld)).toList(); + if (mismatched.isEmpty) return null; + return 'This request says $network, but ${mismatched.join(' and ')} ' + '${mismatched.length == 1 ? 'is not a' : 'are not'} $network account name' + '${mismatched.length == 1 ? '' : 's'}. Check which network your hot wallet is on before signing.'; + } + + /// Implicit accounts (64 hex, or `0x` + 40 hex) belong to every network. + static bool _isNamed(String accountId) => !RegExp(r'^([0-9a-f]{64}|0x[0-9a-f]{40})$').hasMatch(accountId); +} diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart new file mode 100644 index 000000000..492fd0870 --- /dev/null +++ b/cold-wallet-app/test/near_display_test.dart @@ -0,0 +1,106 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_cold_wallet/debug/debug_near_payloads.dart'; +import 'package:quantus_cold_wallet/services/near_display.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +NearTransaction _tx({ + String signer = 'alice.testnet', + String receiver = 'bob.testnet', + List actions = const [], +}) => NearTransaction( + signerId: signer, + publicKey: 'ml-dsa-65:abc', + publicKeyBytes: Uint8List(1952), + signsWithMlDsa65: true, + nonce: BigInt.from(42), + receiverId: receiver, + blockHash: '3KLgJ', + hash: Uint8List(32), + actions: actions, +); + +NearAction _action(NearActionKind kind, {bool fullAccess = false}) => + NearAction(kind: kind, fullAccess: fullAccess, methodNames: const []); + +void main() { + final near = BigInt.from(10).pow(24); + + group('NearDisplay.formatNear', () { + test('keeps every significant decimal and drops trailing zeros', () { + expect(NearDisplay.formatNear(near), '1'); + expect(NearDisplay.formatNear(near * BigInt.from(15) ~/ BigInt.from(10)), '1.5'); + expect(NearDisplay.formatNear(BigInt.one), '0.000000000000000000000001'); + expect(NearDisplay.formatNear(BigInt.zero), '0'); + expect(NearDisplay.formatNear(near * BigInt.from(1000000) + BigInt.from(10).pow(23)), '1000000.1'); + }); + }); + + test('formatGas shows whole Tgas, otherwise raw gas', () { + expect(NearDisplay.formatGas(BigInt.from(30) * BigInt.from(10).pow(12)), '30 Tgas'); + expect(NearDisplay.formatGas(BigInt.from(1234)), '1234 gas'); + }); + + test('argsText pretty-prints JSON and falls back to hex', () { + expect(NearDisplay.argsText(Uint8List.fromList(utf8.encode('{"a":1}'))), '{\n "a": 1\n}'); + expect(NearDisplay.argsText(Uint8List.fromList([0xff, 0x01])), '0xff01'); + expect(NearDisplay.argsText(Uint8List(0)), '(none)'); + }); + + test('headline names a single action and counts several', () { + expect(NearDisplay.headline(_tx(actions: [_action(NearActionKind.transfer)])), 'SEND'); + expect( + NearDisplay.headline(_tx(actions: [_action(NearActionKind.addKey, fullAccess: true)])), + 'ADD FULL ACCESS KEY', + ); + expect(NearDisplay.headline(_tx(actions: [_action(NearActionKind.addKey)])), 'ADD FUNCTION CALL KEY'); + expect( + NearDisplay.headline(_tx(actions: [_action(NearActionKind.functionCall), _action(NearActionKind.transfer)])), + '2 ACTIONS', + ); + }); + + test('key, code and account changes are dangerous; sends and calls are not', () { + for (final kind in [ + NearActionKind.addKey, + NearActionKind.deleteKey, + NearActionKind.deleteAccount, + NearActionKind.deployContract, + NearActionKind.stake, + ]) { + expect(NearDisplay.isDangerous(_action(kind)), isTrue, reason: '$kind'); + } + for (final kind in [NearActionKind.transfer, NearActionKind.functionCall, NearActionKind.createAccount]) { + expect(NearDisplay.isDangerous(_action(kind)), isFalse, reason: '$kind'); + } + }); + + group('NearDisplay.networkMismatch', () { + test('is silent when names match the network or are implicit', () { + expect(NearDisplay.networkMismatch(_tx(), 'testnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(signer: 'alice.near', receiver: 'bob.near'), 'mainnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(signer: 'a' * 64, receiver: '0x${'b' * 40}'), 'mainnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(), 'localnet'), isNull); + }); + + test('names each account that does not belong to the network', () { + expect(NearDisplay.networkMismatch(_tx(), 'mainnet'), contains('alice.testnet and bob.testnet are not mainnet')); + expect( + NearDisplay.networkMismatch(_tx(signer: 'alice.near'), 'mainnet'), + contains('bob.testnet is not a mainnet account name'), + ); + }); + }); + + test('debug borsh writer reproduces the near-cli-rs transfer vector byte for byte', () { + // near-cli-rs 0.30.1 `sign-later` for alice.testnet -> bob.testnet, 1 NEAR, + // ed25519 key 0x11*32, nonce 42, block hash 0x22*32. + const golden = + 'DQAAAGFsaWNlLnRlc3RuZXQAEREREREREREREREREREREREREREREREREREREREREREqAAAAAAAAAAsAAABib2IudGVzdG5ldCIiIiIiIiIi' + 'IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiAQAAAAMAAACh7czOG8LTAAAAAAAA'; + final sample = DebugNearPayloads.samples(null).singleWhere((c) => c.label.startsWith('signed by an ed25519')); + expect(base64Encode(sample.request.transaction), golden); + }); +} diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart new file mode 100644 index 000000000..53744f6c9 --- /dev/null +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -0,0 +1,180 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; +import 'package:quantus_cold_wallet/screens/sign_near_transaction_screen.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +const owner = 'qzmNaLjPU7hcvkjHpGmrVDPD9y12vdAFimCSrP1GkhVFJMaUq'; + +class _UnlockedController extends WalletController { + @override + WalletState build() => const WalletState(status: WalletStatus.unlocked, mnemonic: 'unused in these tests'); +} + +class _LockedController extends WalletController { + @override + WalletState build() => const WalletState(status: WalletStatus.locked); +} + +NearTransaction _tx({ + required List actions, + String signer = 'alice.testnet', + String receiver = 'bob.testnet', + bool mlDsa = true, +}) => NearTransaction( + signerId: signer, + publicKey: mlDsa ? 'ml-dsa-65:7Kq' : 'ed25519:4rN', + publicKeyBytes: Uint8List(mlDsa ? 1952 : 32), + signsWithMlDsa65: mlDsa, + nonce: BigInt.from(42), + receiverId: receiver, + blockHash: '3KLgJ', + hash: Uint8List(32), + actions: actions, +); + +final _near = BigInt.from(10).pow(24); + +NearAction _transfer(BigInt amount) => + NearAction(kind: NearActionKind.transfer, amount: amount, fullAccess: false, methodNames: const []); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const colors = AppColorsV3.dark(); + + Future pump( + WidgetTester tester, + NearTransaction tx, { + String network = 'testnet', + String? keyOwner = owner, + WalletController Function() controller = _UnlockedController.new, + }) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + walletControllerProvider.overrideWith(controller), + nearKeyOwnerProvider.overrideWith((ref, _) => keyOwner), + checksumNameProvider.overrideWith((ref, _) async => 'amber glacier quartz'), + ], + child: MaterialApp( + home: Builder( + builder: (context) => Theme( + data: AppTheme.darkTheme(context), + child: SignNearTransactionScreen( + request: NearSigningRequest(network: network, transaction: Uint8List.fromList([1, 2, 3])), + transaction: tx, + ), + ), + ), + ), + ), + ); + await tester.pumpAndSettle(); + } + + testWidgets('a plain send leads with the amount, the receiver and the key holder', (tester) async { + await pump(tester, _tx(actions: [_transfer(_near * BigInt.from(15) ~/ BigInt.from(10))])); + + expect(find.text('NEAR · TESTNET'), findsOneWidget); + final headline = tester.widget(find.text('SEND')); + expect(headline.style?.color, colors.accentFlare); + expect(find.textContaining('1.5', findRichText: true), findsOneWidget); + expect(find.text('TO'), findsOneWidget); + expect(find.text('bob.testnet'), findsOneWidget); + expect(find.text('FROM'), findsOneWidget); + expect(find.text('alice.testnet'), findsOneWidget); + expect(find.text('SIGNING KEY HELD BY'), findsOneWidget); + expect(find.text(owner), findsOneWidget); + expect(find.text('amber glacier quartz'), findsOneWidget); + expect(find.text('Sign'), findsOneWidget); + expect(find.textContaining('Network does not match'), findsNothing); + expect(find.textContaining('changes control'), findsNothing); + }); + + testWidgets('a full access key is headlined as dangerous with every parameter listed', (tester) async { + await pump( + tester, + _tx( + receiver: 'alice.testnet', + actions: [ + const NearAction(kind: NearActionKind.addKey, publicKey: 'ed25519:4rN', fullAccess: true, methodNames: []), + ], + ), + ); + + final headline = tester.widget(find.text('ADD FULL ACCESS KEY')); + expect(headline.style?.color, colors.semanticEmber); + expect(find.text('This changes control of alice.testnet'), findsOneWidget); + expect(find.text('1. ADD FULL ACCESS KEY'), findsOneWidget); + expect(find.text('ed25519:4rN'), findsOneWidget); + expect(find.text('Full access'), findsOneWidget); + expect(find.text('Sign'), findsOneWidget); + }); + + testWidgets('every action of a multi-action call is listed with its own parameters', (tester) async { + await pump( + tester, + _tx( + receiver: 'ref-finance.testnet', + actions: [ + NearAction( + kind: NearActionKind.functionCall, + target: 'storage_deposit', + args: Uint8List.fromList('{}'.codeUnits), + gas: BigInt.from(30) * BigInt.from(10).pow(12), + amount: _near ~/ BigInt.from(80), + fullAccess: false, + methodNames: const [], + ), + _transfer(_near), + ], + ), + ); + + expect(find.text('2 ACTIONS'), findsOneWidget); + expect(find.text('1. CALL CONTRACT'), findsOneWidget); + expect(find.text('storage_deposit'), findsOneWidget); + expect(find.text('30 Tgas'), findsOneWidget); + expect(find.text('0.0125 NEAR'), findsOneWidget); + expect(find.text('2. SEND'), findsOneWidget); + expect(find.text('1 NEAR'), findsOneWidget); + expect(find.text('ref-finance.testnet'), findsOneWidget); + }); + + testWidgets('warns when the account names do not belong to the labelled network', (tester) async { + await pump(tester, _tx(actions: [_transfer(_near)]), network: 'mainnet'); + + expect(find.text('NEAR · MAINNET'), findsOneWidget); + expect(find.text('Network does not match'), findsOneWidget); + expect(find.textContaining('alice.testnet and bob.testnet are not mainnet'), findsOneWidget); + expect(find.text('Sign'), findsOneWidget); + }); + + testWidgets('refuses a key no account here holds, and offers no Sign', (tester) async { + await pump(tester, _tx(actions: [_transfer(_near)]), keyOwner: null); + + expect(find.text('Key not in this wallet'), findsOneWidget); + expect(find.text('ml-dsa-65:7Kq'), findsOneWidget); + expect(find.text('Nothing was signed.'), findsOneWidget); + expect(find.text('Sign'), findsNothing); + }); + + testWidgets('refuses a transaction for a non-ML-DSA-65 key before looking up any account', (tester) async { + await pump(tester, _tx(actions: [_transfer(_near)], mlDsa: false)); + + expect(find.text('Not a Quantus key'), findsOneWidget); + expect(find.textContaining('names a key of type ed25519'), findsOneWidget); + expect(find.text('Sign'), findsNothing); + }); + + testWidgets('a locked wallet is told to unlock, not shown a review', (tester) async { + await pump(tester, _tx(actions: [_transfer(_near)]), controller: _LockedController.new); + + expect(find.text('Wallet is locked'), findsOneWidget); + expect(find.text('Sign'), findsNothing); + }); +} From 6caece732bd026e034fd31d9d638659290749188 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:10:47 +0000 Subject: [PATCH 04/16] sdk: format regenerated bindings at the repo's 120-column width --- quantus_sdk/lib/src/rust/api/airdrop.dart | 25 +- quantus_sdk/lib/src/rust/api/crypto.dart | 50 +- quantus_sdk/lib/src/rust/api/multisig.dart | 11 +- quantus_sdk/lib/src/rust/api/near.dart | 29 +- quantus_sdk/lib/src/rust/api/ur.dart | 11 +- quantus_sdk/lib/src/rust/api/wormhole.dart | 63 +- quantus_sdk/lib/src/rust/frb_generated.dart | 823 ++++-------------- .../lib/src/rust/frb_generated.io.dart | 119 +-- .../lib/src/rust/frb_generated.web.dart | 120 +-- 9 files changed, 303 insertions(+), 948 deletions(-) diff --git a/quantus_sdk/lib/src/rust/api/airdrop.dart b/quantus_sdk/lib/src/rust/api/airdrop.dart index d6485eeab..187942698 100644 --- a/quantus_sdk/lib/src/rust/api/airdrop.dart +++ b/quantus_sdk/lib/src/rust/api/airdrop.dart @@ -56,25 +56,22 @@ Future buildAirdropDilithiumClaimFromMnemonic({ required String dilithiumKeygen, required String address, required String claimAccount, -}) => - RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic( - mnemonic: mnemonic, - dilithiumKeygen: dilithiumKeygen, - address: address, - claimAccount: claimAccount, - ); +}) => RustLib.instance.api.crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic( + mnemonic: mnemonic, + dilithiumKeygen: dilithiumKeygen, + address: address, + claimAccount: claimAccount, +); /// Prove ownership of the wormhole address derived from `wormhole_secret` /// (current rate-8 scheme), binding the payout to `claim_account`. Builds the /// circuit in-process: expect tens of seconds of CPU on first call. Submit as /// `{"kind": "wormhole", ...fields}`. -Future proveAirdropWormhole({ - required List wormholeSecret, - required String claimAccount, -}) => RustLib.instance.api.crateApiAirdropProveAirdropWormhole( - wormholeSecret: wormholeSecret, - claimAccount: claimAccount, -); +Future proveAirdropWormhole({required List wormholeSecret, required String claimAccount}) => + RustLib.instance.api.crateApiAirdropProveAirdropWormhole( + wormholeSecret: wormholeSecret, + claimAccount: claimAccount, + ); /// A snapshot address this wallet can prove ownership of. class AirdropMatch { diff --git a/quantus_sdk/lib/src/rust/api/crypto.dart b/quantus_sdk/lib/src/rust/api/crypto.dart index ab13d7b3c..4f7701bcf 100644 --- a/quantus_sdk/lib/src/rust/api/crypto.dart +++ b/quantus_sdk/lib/src/rust/api/crypto.dart @@ -14,44 +14,28 @@ void setDefaultSs58Prefix({required int prefix}) => RustLib.instance.api.crateApiCryptoSetDefaultSs58Prefix(prefix: prefix); /// Convert public key to accountId32 in ss58check format -String toAccountId({required Keypair obj}) => - RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); +String toAccountId({required Keypair obj}) => RustLib.instance.api.crateApiCryptoToAccountId(obj: obj); /// Convert key in ss58check format to accountId32 -Uint8List ss58ToAccountId({required String s}) => - RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); +Uint8List ss58ToAccountId({required String s}) => RustLib.instance.api.crateApiCryptoSs58ToAccountId(s: s); /// Legacy non-HD ML-DSA-87 keypair straight from the mnemonic seed (early CLI and miner accounts). -Keypair generateKeypair({required String mnemonicStr}) => RustLib.instance.api - .crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); - -Keypair generateDerivedKeypair({ - required String mnemonicStr, - required String path, - required DilithiumScheme scheme, -}) => RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair( - mnemonicStr: mnemonicStr, - path: path, - scheme: scheme, -); +Keypair generateKeypair({required String mnemonicStr}) => + RustLib.instance.api.crateApiCryptoGenerateKeypair(mnemonicStr: mnemonicStr); -WormholeResult deriveWormhole({ - required String mnemonicStr, - required String path, -}) => RustLib.instance.api.crateApiCryptoDeriveWormhole( - mnemonicStr: mnemonicStr, - path: path, -); +Keypair generateDerivedKeypair({required String mnemonicStr, required String path, required DilithiumScheme scheme}) => + RustLib.instance.api.crateApiCryptoGenerateDerivedKeypair(mnemonicStr: mnemonicStr, path: path, scheme: scheme); + +WormholeResult deriveWormhole({required String mnemonicStr, required String path}) => + RustLib.instance.api.crateApiCryptoDeriveWormhole(mnemonicStr: mnemonicStr, path: path); /// Convert a first_hash (rewards preimage) to its corresponding wormhole address. /// /// Mirrors how the chain and ZK circuit derive the address from the preimage: /// - Convert 32 bytes → 4 Poseidon field elements (8 bytes each) /// - Hash once without padding -String firstHashToAddress({required String firstHashHex}) => RustLib - .instance - .api - .crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); +String firstHashToAddress({required String firstHashHex}) => + RustLib.instance.api.crateApiCryptoFirstHashToAddress(firstHashHex: firstHashHex); /// ML-DSA-87 keypair from a raw 32-byte seed (dev accounts). Keypair generateKeypairFromSeed({required List seed}) => @@ -122,11 +106,7 @@ class Keypair { final Uint8List secretKey; final DilithiumScheme scheme; - const Keypair({ - required this.publicKey, - required this.secretKey, - required this.scheme, - }); + const Keypair({required this.publicKey, required this.secretKey, required this.scheme}); @override int get hashCode => publicKey.hashCode ^ secretKey.hashCode ^ scheme.hashCode; @@ -146,11 +126,7 @@ class WormholeResult { final Uint8List firstHash; final Uint8List secret; - const WormholeResult({ - required this.address, - required this.firstHash, - required this.secret, - }); + const WormholeResult({required this.address, required this.firstHash, required this.secret}); @override int get hashCode => address.hashCode ^ firstHash.hashCode ^ secret.hashCode; diff --git a/quantus_sdk/lib/src/rust/api/multisig.dart b/quantus_sdk/lib/src/rust/api/multisig.dart index cfec97749..8fcb105f9 100644 --- a/quantus_sdk/lib/src/rust/api/multisig.dart +++ b/quantus_sdk/lib/src/rust/api/multisig.dart @@ -11,12 +11,5 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; /// Predicts the on-chain multisig address for the given signers, threshold, and nonce. /// /// Address is computed as: hash(pallet_id || sorted_signers || threshold || nonce) -String predictMultisigAddress({ - required List signers, - required int threshold, - required BigInt nonce, -}) => RustLib.instance.api.crateApiMultisigPredictMultisigAddress( - signers: signers, - threshold: threshold, - nonce: nonce, -); +String predictMultisigAddress({required List signers, required int threshold, required BigInt nonce}) => + RustLib.instance.api.crateApiMultisigPredictMultisigAddress(signers: signers, threshold: threshold, nonce: nonce); diff --git a/quantus_sdk/lib/src/rust/api/near.dart b/quantus_sdk/lib/src/rust/api/near.dart index 6224dc0ca..9cb60a215 100644 --- a/quantus_sdk/lib/src/rust/api/near.dart +++ b/quantus_sdk/lib/src/rust/api/near.dart @@ -8,34 +8,23 @@ import '../lib.dart'; import 'crypto.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; -// These functions are ignored because they are not marked as `pub`: `bytes`, `decode_wire`, `require_ml_dsa_65`, `text` +// These functions are ignored because they are not marked as `pub`: `bytes`, `check_account_id`, `decode_wire`, `require_ml_dsa_65`, `text` // These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `WireAccessKeyPermission`, `WireAccessKey`, `WireAction`, `WireFunctionCallPermission`, `WirePublicKey`, `WireTransaction` // These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `assert_receiver_is_total_eq`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `clone`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_reader`, `deserialize_variant`, `deserialize_variant`, `deserialize_variant`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `eq`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `fmt`, `from`, `serialize`, `serialize`, `serialize`, `serialize`, `serialize`, `serialize` /// Decode a borsh `TransactionV0`. Refuses trailing bytes. NearTransaction decodeNearTransaction({required List transaction}) => - RustLib.instance.api.crateApiNearDecodeNearTransaction( - transaction: transaction, - ); + RustLib.instance.api.crateApiNearDecodeNearTransaction(transaction: transaction); /// SHA-256 of the transaction bytes: the message NEAR signs and verifies. -Uint8List nearTransactionHash({required List transaction}) => RustLib - .instance - .api - .crateApiNearNearTransactionHash(transaction: transaction); +Uint8List nearTransactionHash({required List transaction}) => + RustLib.instance.api.crateApiNearNearTransactionHash(transaction: transaction); /// Sign a borsh `TransactionV0` for NEAR. Refuses unless `keypair` is /// ML-DSA-65 *and* is the key the transaction declares. Returns /// `signature ‖ public_key` (3309 + 1952 bytes). -Uint8List signNearTransaction({ - required Keypair keypair, - required List transaction, - U8Array32? entropy, -}) => RustLib.instance.api.crateApiNearSignNearTransaction( - keypair: keypair, - transaction: transaction, - entropy: entropy, -); +Uint8List signNearTransaction({required Keypair keypair, required List transaction, U8Array32? entropy}) => + RustLib.instance.api.crateApiNearSignNearTransaction(keypair: keypair, transaction: transaction, entropy: entropy); /// Whether `signature` is a valid pure ML-DSA-65 signature by `public_key` /// over the transaction, as the NEAR runtime checks it. @@ -106,8 +95,7 @@ class NearAction { this.codeLen, }); - static Future default_() => - RustLib.instance.api.crateApiNearNearActionDefault(); + static Future default_() => RustLib.instance.api.crateApiNearNearActionDefault(); @override int get hashCode => @@ -149,8 +137,7 @@ enum NearActionKind { deleteKey, deleteAccount; - static Future default_() => - RustLib.instance.api.crateApiNearNearActionKindDefault(); + static Future default_() => RustLib.instance.api.crateApiNearNearActionKindDefault(); } /// A decoded NEAR transaction, ready to display. diff --git a/quantus_sdk/lib/src/rust/api/ur.dart b/quantus_sdk/lib/src/rust/api/ur.dart index cb91f5267..7a82bc0f2 100644 --- a/quantus_sdk/lib/src/rust/api/ur.dart +++ b/quantus_sdk/lib/src/rust/api/ur.dart @@ -12,14 +12,9 @@ int maxUrParts() => RustLib.instance.api.crateApiUrMaxUrParts(); int maxUrPartChars() => RustLib.instance.api.crateApiUrMaxUrPartChars(); -Uint8List decodeUr({required List urParts}) => - RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); +Uint8List decodeUr({required List urParts}) => RustLib.instance.api.crateApiUrDecodeUr(urParts: urParts); List encodeUr({required List data, int? maxFragmentLength}) => - RustLib.instance.api.crateApiUrEncodeUr( - data: data, - maxFragmentLength: maxFragmentLength, - ); + RustLib.instance.api.crateApiUrEncodeUr(data: data, maxFragmentLength: maxFragmentLength); -bool isCompleteUr({required List urParts}) => - RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); +bool isCompleteUr({required List urParts}) => RustLib.instance.api.crateApiUrIsCompleteUr(urParts: urParts); diff --git a/quantus_sdk/lib/src/rust/api/wormhole.dart b/quantus_sdk/lib/src/rust/api/wormhole.dart index ff954fb24..b94bb0597 100644 --- a/quantus_sdk/lib/src/rust/api/wormhole.dart +++ b/quantus_sdk/lib/src/rust/api/wormhole.dart @@ -8,45 +8,28 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // These functions are ignored because they are not marked as `pub`: `all_required_files_exist`, `cleanup_stale_circuit_dirs`, `vec_to_32`, `vec_to_digest`, `versioned_bins_dir` -String zkCircuitsVersion() => - RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); - -String computeAddressHashHex({required List rawAddress}) => RustLib - .instance - .api - .crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); - -Uint8List computeNullifier({ - required List secret, - required BigInt transferCount, -}) => RustLib.instance.api.crateApiWormholeComputeNullifier( - secret: secret, - transferCount: transferCount, -); +String zkCircuitsVersion() => RustLib.instance.api.crateApiWormholeZkCircuitsVersion(); + +String computeAddressHashHex({required List rawAddress}) => + RustLib.instance.api.crateApiWormholeComputeAddressHashHex(rawAddress: rawAddress); + +Uint8List computeNullifier({required List secret, required BigInt transferCount}) => + RustLib.instance.api.crateApiWormholeComputeNullifier(secret: secret, transferCount: transferCount); Uint8List computeWormholeAddress({required List secret}) => RustLib.instance.api.crateApiWormholeComputeWormholeAddress(secret: secret); -int wormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, -}) => RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount( - inputAmount: inputAmount, - feeBps: feeBps, -); +int wormholeComputeOutputAmount({required int inputAmount, required int feeBps}) => + RustLib.instance.api.crateApiWormholeWormholeComputeOutputAmount(inputAmount: inputAmount, feeBps: feeBps); int decodeLeafAmount({required List leafData}) => RustLib.instance.api.crateApiWormholeDecodeLeafAmount(leafData: leafData); -BigInt decodeLeafTransferCount({required List leafData}) => RustLib - .instance - .api - .crateApiWormholeDecodeLeafTransferCount(leafData: leafData); +BigInt decodeLeafTransferCount({required List leafData}) => + RustLib.instance.api.crateApiWormholeDecodeLeafTransferCount(leafData: leafData); -Uint8List decodeLeafToAccount({required List leafData}) => RustLib - .instance - .api - .crateApiWormholeDecodeLeafToAccount(leafData: leafData); +Uint8List decodeLeafToAccount({required List leafData}) => + RustLib.instance.api.crateApiWormholeDecodeLeafToAccount(leafData: leafData); MerkleProcessed computeMerklePositions({ required List unsortedSiblingsFlat, @@ -58,10 +41,8 @@ MerkleProcessed computeMerklePositions({ depth: depth, ); -Future ensureCircuitBinaries({required String binsDir}) => RustLib - .instance - .api - .crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); +Future ensureCircuitBinaries({required String binsDir}) => + RustLib.instance.api.crateApiWormholeEnsureCircuitBinaries(binsDir: binsDir); Future generateProof({ required ProofInput input, @@ -73,22 +54,14 @@ Future generateProof({ commonBinPath: commonBinPath, ); -Future aggregateProofs({ - required List proofBytesList, - required String binsDir, -}) => RustLib.instance.api.crateApiWormholeAggregateProofs( - proofBytesList: proofBytesList, - binsDir: binsDir, -); +Future aggregateProofs({required List proofBytesList, required String binsDir}) => + RustLib.instance.api.crateApiWormholeAggregateProofs(proofBytesList: proofBytesList, binsDir: binsDir); class MerkleProcessed { final Uint8List sortedSiblingsFlat; final Uint8List positions; - const MerkleProcessed({ - required this.sortedSiblingsFlat, - required this.positions, - }); + const MerkleProcessed({required this.sortedSiblingsFlat, required this.positions}); @override int get hashCode => sortedSiblingsFlat.hashCode ^ positions.hashCode; diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index 36115e167..866eabd6d 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -12,8 +12,7 @@ import 'api/wormhole.dart'; import 'dart:async'; import 'dart:convert'; import 'frb_generated.dart'; -import 'frb_generated.io.dart' - if (dart.library.js_interop) 'frb_generated.web.dart'; +import 'frb_generated.io.dart' if (dart.library.js_interop) 'frb_generated.web.dart'; import 'lib.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; @@ -52,12 +51,10 @@ class RustLib extends BaseEntrypoint { static void dispose() => instance.disposeImpl(); @override - ApiImplConstructor get apiImplConstructor => - RustLibApiImpl.new; + ApiImplConstructor get apiImplConstructor => RustLibApiImpl.new; @override - WireConstructor get wireConstructor => - RustLibWire.fromExternalLibrary; + WireConstructor get wireConstructor => RustLibWire.fromExternalLibrary; @override Future executeRustInitializers() async { @@ -65,8 +62,7 @@ class RustLib extends BaseEntrypoint { } @override - ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => - kDefaultExternalLibraryLoaderConfig; + ExternalLibraryLoaderConfig get defaultExternalLibraryLoaderConfig => kDefaultExternalLibraryLoaderConfig; @override String get codegenVersion => '2.12.0'; @@ -74,20 +70,16 @@ class RustLib extends BaseEntrypoint { @override int get rustContentHash => -91816623; - static const kDefaultExternalLibraryLoaderConfig = - ExternalLibraryLoaderConfig( - stem: 'rust_lib_quantus_wallet', - ioDirectory: 'rust/target/release/', - webPrefix: 'pkg/', - wasmBindgenName: 'wasm_bindgen', - ); + static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( + stem: 'rust_lib_quantus_wallet', + ioDirectory: 'rust/target/release/', + webPrefix: 'pkg/', + wasmBindgenName: 'wasm_bindgen', + ); } abstract class RustLibApi extends BaseApi { - Future crateApiWormholeAggregateProofs({ - required List proofBytesList, - required String binsDir, - }); + Future crateApiWormholeAggregateProofs({required List proofBytesList, required String binsDir}); Future crateApiAirdropBuildAirdropDilithiumClaim({ required Keypair keypair, @@ -95,8 +87,7 @@ abstract class RustLibApi extends BaseApi { required String claimAccount, }); - Future - crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ required String mnemonic, required String dilithiumKeygen, required String address, @@ -111,10 +102,7 @@ abstract class RustLibApi extends BaseApi { required int depth, }); - Uint8List crateApiWormholeComputeNullifier({ - required List secret, - required BigInt transferCount, - }); + Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}); Uint8List crateApiWormholeComputeWormholeAddress({required List secret}); @@ -130,25 +118,15 @@ abstract class RustLibApi extends BaseApi { BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}); - NearTransaction crateApiNearDecodeNearTransaction({ - required List transaction, - }); + NearTransaction crateApiNearDecodeNearTransaction({required List transaction}); Uint8List crateApiUrDecodeUr({required List urParts}); - WormholeResult crateApiCryptoDeriveWormhole({ - required String mnemonicStr, - required String path, - }); + WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}); - List crateApiUrEncodeUr({ - required List data, - int? maxFragmentLength, - }); + List crateApiUrEncodeUr({required List data, int? maxFragmentLength}); - Future crateApiWormholeEnsureCircuitBinaries({ - required String binsDir, - }); + Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}); Future> crateApiAirdropFindAirdropMatches({ required List snapshotAddresses, @@ -249,21 +227,15 @@ abstract class RustLibApi extends BaseApi { required List signature, }); - int crateApiWormholeWormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, - }); + int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}); String crateApiWormholeZkCircuitsVersion(); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_HdLatticeError; + RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError; - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_HdLatticeError; + RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError; - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr; + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -285,17 +257,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_list_prim_u_8_strict(proofBytesList, serializer); sse_encode_String(binsDir, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 1, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 1, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeAggregateProofsConstMeta, argValues: [proofBytesList, binsDir], apiImpl: this, @@ -304,10 +268,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeAggregateProofsConstMeta => - const TaskConstMeta( - debugName: 'aggregate_proofs', - argNames: ['proofBytesList', 'binsDir'], - ); + const TaskConstMeta(debugName: 'aggregate_proofs', argNames: ['proofBytesList', 'binsDir']); @override Future crateApiAirdropBuildAirdropDilithiumClaim({ @@ -322,17 +283,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(keypair, serializer); sse_encode_String(address, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 2, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 2, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_dilithium_claim_body, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta, argValues: [keypair, address, claimAccount], apiImpl: this, @@ -341,14 +294,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimConstMeta => - const TaskConstMeta( - debugName: 'build_airdrop_dilithium_claim', - argNames: ['keypair', 'address', 'claimAccount'], - ); + const TaskConstMeta(debugName: 'build_airdrop_dilithium_claim', argNames: ['keypair', 'address', 'claimAccount']); @override - Future - crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ + Future crateApiAirdropBuildAirdropDilithiumClaimFromMnemonic({ required String mnemonic, required String dilithiumKeygen, required String address, @@ -362,36 +311,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(dilithiumKeygen, serializer); sse_encode_String(address, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 3, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 3, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_dilithium_claim_body, - decodeErrorData: sse_decode_String, - ), - constMeta: - kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta, + codec: SseCodec(decodeSuccessData: sse_decode_dilithium_claim_body, decodeErrorData: sse_decode_String), + constMeta: kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta, argValues: [mnemonic, dilithiumKeygen, address, claimAccount], apiImpl: this, ), ); } - TaskConstMeta - get kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta => - const TaskConstMeta( - debugName: 'build_airdrop_dilithium_claim_from_mnemonic', - argNames: ['mnemonic', 'dilithiumKeygen', 'address', 'claimAccount'], - ); + TaskConstMeta get kCrateApiAirdropBuildAirdropDilithiumClaimFromMnemonicConstMeta => const TaskConstMeta( + debugName: 'build_airdrop_dilithium_claim_from_mnemonic', + argNames: ['mnemonic', 'dilithiumKeygen', 'address', 'claimAccount'], + ); @override - String crateApiWormholeComputeAddressHashHex({ - required List rawAddress, - }) { + String crateApiWormholeComputeAddressHashHex({required List rawAddress}) { return handler.executeSync( SyncTask( callFfi: () { @@ -399,10 +335,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(rawAddress, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 4)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeAddressHashHexConstMeta, argValues: [rawAddress], apiImpl: this, @@ -411,10 +344,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeAddressHashHexConstMeta => - const TaskConstMeta( - debugName: 'compute_address_hash_hex', - argNames: ['rawAddress'], - ); + const TaskConstMeta(debugName: 'compute_address_hash_hex', argNames: ['rawAddress']); @override MerkleProcessed crateApiWormholeComputeMerklePositions({ @@ -431,10 +361,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(depth, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 5)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_merkle_processed, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_merkle_processed, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeMerklePositionsConstMeta, argValues: [unsortedSiblingsFlat, leafHash, depth], apiImpl: this, @@ -442,17 +369,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => - const TaskConstMeta( - debugName: 'compute_merkle_positions', - argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], - ); + TaskConstMeta get kCrateApiWormholeComputeMerklePositionsConstMeta => const TaskConstMeta( + debugName: 'compute_merkle_positions', + argNames: ['unsortedSiblingsFlat', 'leafHash', 'depth'], + ); @override - Uint8List crateApiWormholeComputeNullifier({ - required List secret, - required BigInt transferCount, - }) { + Uint8List crateApiWormholeComputeNullifier({required List secret, required BigInt transferCount}) { return handler.executeSync( SyncTask( callFfi: () { @@ -461,10 +384,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(transferCount, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 6)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeNullifierConstMeta, argValues: [secret, transferCount], apiImpl: this, @@ -473,15 +393,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeNullifierConstMeta => - const TaskConstMeta( - debugName: 'compute_nullifier', - argNames: ['secret', 'transferCount'], - ); + const TaskConstMeta(debugName: 'compute_nullifier', argNames: ['secret', 'transferCount']); @override - Uint8List crateApiWormholeComputeWormholeAddress({ - required List secret, - }) { + Uint8List crateApiWormholeComputeWormholeAddress({required List secret}) { return handler.executeSync( SyncTask( callFfi: () { @@ -489,10 +404,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(secret, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 7)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeComputeWormholeAddressConstMeta, argValues: [secret], apiImpl: this, @@ -501,10 +413,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeComputeWormholeAddressConstMeta => - const TaskConstMeta( - debugName: 'compute_wormhole_address', - argNames: ['secret'], - ); + const TaskConstMeta(debugName: 'compute_wormhole_address', argNames: ['secret']); @override Keypair crateApiCryptoCrystalAlice() { @@ -514,10 +423,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 8)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalAliceConstMeta, argValues: [], apiImpl: this, @@ -536,10 +442,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 9)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalBobConstMeta, argValues: [], apiImpl: this, @@ -547,8 +450,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => - const TaskConstMeta(debugName: 'crystal_bob', argNames: []); + TaskConstMeta get kCrateApiCryptoCrystalBobConstMeta => const TaskConstMeta(debugName: 'crystal_bob', argNames: []); @override Keypair crateApiCryptoCrystalCharlie() { @@ -558,10 +460,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 10)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoCrystalCharlieConstMeta, argValues: [], apiImpl: this, @@ -581,10 +480,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 11)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafAmountConstMeta, argValues: [leafData], apiImpl: this, @@ -593,10 +489,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafAmountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_amount', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_amount', argNames: ['leafData']); @override Uint8List crateApiWormholeDecodeLeafToAccount({required List leafData}) { @@ -607,10 +500,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 12)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafToAccountConstMeta, argValues: [leafData], apiImpl: this, @@ -619,15 +509,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafToAccountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_to_account', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_to_account', argNames: ['leafData']); @override - BigInt crateApiWormholeDecodeLeafTransferCount({ - required List leafData, - }) { + BigInt crateApiWormholeDecodeLeafTransferCount({required List leafData}) { return handler.executeSync( SyncTask( callFfi: () { @@ -635,10 +520,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(leafData, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 13)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_64, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_64, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeDecodeLeafTransferCountConstMeta, argValues: [leafData], apiImpl: this, @@ -647,15 +529,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeDecodeLeafTransferCountConstMeta => - const TaskConstMeta( - debugName: 'decode_leaf_transfer_count', - argNames: ['leafData'], - ); + const TaskConstMeta(debugName: 'decode_leaf_transfer_count', argNames: ['leafData']); @override - NearTransaction crateApiNearDecodeNearTransaction({ - required List transaction, - }) { + NearTransaction crateApiNearDecodeNearTransaction({required List transaction}) { return handler.executeSync( SyncTask( callFfi: () { @@ -663,10 +540,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(transaction, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 14)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_near_transaction, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_near_transaction, decodeErrorData: sse_decode_String), constMeta: kCrateApiNearDecodeNearTransactionConstMeta, argValues: [transaction], apiImpl: this, @@ -675,10 +549,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearDecodeNearTransactionConstMeta => - const TaskConstMeta( - debugName: 'decode_near_transaction', - argNames: ['transaction'], - ); + const TaskConstMeta(debugName: 'decode_near_transaction', argNames: ['transaction']); @override Uint8List crateApiUrDecodeUr({required List urParts}) { @@ -689,10 +560,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(urParts, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 15)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrDecodeUrConstMeta, argValues: [urParts], apiImpl: this, @@ -700,14 +568,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrDecodeUrConstMeta => - const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); + TaskConstMeta get kCrateApiUrDecodeUrConstMeta => const TaskConstMeta(debugName: 'decode_ur', argNames: ['urParts']); @override - WormholeResult crateApiCryptoDeriveWormhole({ - required String mnemonicStr, - required String path, - }) { + WormholeResult crateApiCryptoDeriveWormhole({required String mnemonicStr, required String path}) { return handler.executeSync( SyncTask( callFfi: () { @@ -729,16 +593,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoDeriveWormholeConstMeta => - const TaskConstMeta( - debugName: 'derive_wormhole', - argNames: ['mnemonicStr', 'path'], - ); + const TaskConstMeta(debugName: 'derive_wormhole', argNames: ['mnemonicStr', 'path']); @override - List crateApiUrEncodeUr({ - required List data, - int? maxFragmentLength, - }) { + List crateApiUrEncodeUr({required List data, int? maxFragmentLength}) { return handler.executeSync( SyncTask( callFfi: () { @@ -747,10 +605,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_box_autoadd_u_32(maxFragmentLength, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 17)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiUrEncodeUrConstMeta, argValues: [data, maxFragmentLength], apiImpl: this, @@ -758,31 +613,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrEncodeUrConstMeta => const TaskConstMeta( - debugName: 'encode_ur', - argNames: ['data', 'maxFragmentLength'], - ); + TaskConstMeta get kCrateApiUrEncodeUrConstMeta => + const TaskConstMeta(debugName: 'encode_ur', argNames: ['data', 'maxFragmentLength']); @override - Future crateApiWormholeEnsureCircuitBinaries({ - required String binsDir, - }) { + Future crateApiWormholeEnsureCircuitBinaries({required String binsDir}) { return handler.executeNormal( NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_String(binsDir, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 18, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 18, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeEnsureCircuitBinariesConstMeta, argValues: [binsDir], apiImpl: this, @@ -791,10 +634,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeEnsureCircuitBinariesConstMeta => - const TaskConstMeta( - debugName: 'ensure_circuit_binaries', - argNames: ['binsDir'], - ); + const TaskConstMeta(debugName: 'ensure_circuit_binaries', argNames: ['binsDir']); @override Future> crateApiAirdropFindAirdropMatches({ @@ -810,43 +650,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(snapshotAddresses, serializer); sse_encode_opt_list_prim_u_8_strict(dilithiumPublicKey, serializer); sse_encode_opt_String(mnemonic, serializer); - sse_encode_list_list_prim_u_8_strict( - extraWormholeSecrets, - serializer, - ); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 19, - port: port_, - ); + sse_encode_list_list_prim_u_8_strict(extraWormholeSecrets, serializer); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 19, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_airdrop_match, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_airdrop_match, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropFindAirdropMatchesConstMeta, - argValues: [ - snapshotAddresses, - dilithiumPublicKey, - mnemonic, - extraWormholeSecrets, - ], + argValues: [snapshotAddresses, dilithiumPublicKey, mnemonic, extraWormholeSecrets], apiImpl: this, ), ); } - TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => - const TaskConstMeta( - debugName: 'find_airdrop_matches', - argNames: [ - 'snapshotAddresses', - 'dilithiumPublicKey', - 'mnemonic', - 'extraWormholeSecrets', - ], - ); + TaskConstMeta get kCrateApiAirdropFindAirdropMatchesConstMeta => const TaskConstMeta( + debugName: 'find_airdrop_matches', + argNames: ['snapshotAddresses', 'dilithiumPublicKey', 'mnemonic', 'extraWormholeSecrets'], + ); @override String crateApiCryptoFirstHashToAddress({required String firstHashHex}) { @@ -857,10 +675,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(firstHashHex, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 20)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoFirstHashToAddressConstMeta, argValues: [firstHashHex], apiImpl: this, @@ -869,10 +684,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoFirstHashToAddressConstMeta => - const TaskConstMeta( - debugName: 'first_hash_to_address', - argNames: ['firstHashHex'], - ); + const TaskConstMeta(debugName: 'first_hash_to_address', argNames: ['firstHashHex']); @override Keypair crateApiCryptoGenerateDerivedKeypair({ @@ -902,10 +714,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateDerivedKeypairConstMeta => - const TaskConstMeta( - debugName: 'generate_derived_keypair', - argNames: ['mnemonicStr', 'path', 'scheme'], - ); + const TaskConstMeta(debugName: 'generate_derived_keypair', argNames: ['mnemonicStr', 'path', 'scheme']); @override Keypair crateApiCryptoGenerateKeypair({required String mnemonicStr}) { @@ -929,10 +738,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairConstMeta => - const TaskConstMeta( - debugName: 'generate_keypair', - argNames: ['mnemonicStr'], - ); + const TaskConstMeta(debugName: 'generate_keypair', argNames: ['mnemonicStr']); @override Keypair crateApiCryptoGenerateKeypairFromSeed({required List seed}) { @@ -943,10 +749,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(seed, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 23)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_keypair, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_keypair, decodeErrorData: null), constMeta: kCrateApiCryptoGenerateKeypairFromSeedConstMeta, argValues: [seed], apiImpl: this, @@ -955,10 +758,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoGenerateKeypairFromSeedConstMeta => - const TaskConstMeta( - debugName: 'generate_keypair_from_seed', - argNames: ['seed'], - ); + const TaskConstMeta(debugName: 'generate_keypair_from_seed', argNames: ['seed']); @override Future crateApiWormholeGenerateProof({ @@ -973,17 +773,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_proof_input(input, serializer); sse_encode_String(proverBinPath, serializer); sse_encode_String(commonBinPath, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 24, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 24, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_proof_output, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_proof_output, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeGenerateProofConstMeta, argValues: [input, proverBinPath, commonBinPath], apiImpl: this, @@ -992,10 +784,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeGenerateProofConstMeta => - const TaskConstMeta( - debugName: 'generate_proof', - argNames: ['input', 'proverBinPath', 'commonBinPath'], - ); + const TaskConstMeta(debugName: 'generate_proof', argNames: ['input', 'proverBinPath', 'commonBinPath']); @override Future crateApiCryptoInitApp() { @@ -1003,17 +792,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 25, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 25, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoInitAppConstMeta, argValues: [], apiImpl: this, @@ -1021,8 +802,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoInitAppConstMeta => - const TaskConstMeta(debugName: 'init_app', argNames: []); + TaskConstMeta get kCrateApiCryptoInitAppConstMeta => const TaskConstMeta(debugName: 'init_app', argNames: []); @override bool crateApiUrIsCompleteUr({required List urParts}) { @@ -1033,10 +813,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_String(urParts, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 26)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_bool, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiUrIsCompleteUrConstMeta, argValues: [urParts], apiImpl: this, @@ -1055,10 +832,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 27)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartCharsConstMeta, argValues: [], apiImpl: this, @@ -1077,10 +851,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 28)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiUrMaxUrPartsConstMeta, argValues: [], apiImpl: this, @@ -1088,8 +859,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => - const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); + TaskConstMeta get kCrateApiUrMaxUrPartsConstMeta => const TaskConstMeta(debugName: 'max_ur_parts', argNames: []); @override Future crateApiNearNearActionDefault() { @@ -1097,17 +867,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 29, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 29, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_near_action, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_near_action, decodeErrorData: null), constMeta: kCrateApiNearNearActionDefaultConstMeta, argValues: [], apiImpl: this, @@ -1124,17 +886,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NormalTask( callFfi: (port_) { final serializer = SseSerializer(generalizedFrbRustBinding); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 30, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 30, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_near_action_kind, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_near_action_kind, decodeErrorData: null), constMeta: kCrateApiNearNearActionKindDefaultConstMeta, argValues: [], apiImpl: this, @@ -1154,10 +908,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(keypair, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 31)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiNearNearPublicKeyHandleConstMeta, argValues: [keypair], apiImpl: this, @@ -1166,10 +917,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearNearPublicKeyHandleConstMeta => - const TaskConstMeta( - debugName: 'near_public_key_handle', - argNames: ['keypair'], - ); + const TaskConstMeta(debugName: 'near_public_key_handle', argNames: ['keypair']); @override String crateApiNearNearPublicKeyText({required Keypair keypair}) { @@ -1180,10 +928,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(keypair, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 32)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiNearNearPublicKeyTextConstMeta, argValues: [keypair], apiImpl: this, @@ -1192,10 +937,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearNearPublicKeyTextConstMeta => - const TaskConstMeta( - debugName: 'near_public_key_text', - argNames: ['keypair'], - ); + const TaskConstMeta(debugName: 'near_public_key_text', argNames: ['keypair']); @override Uint8List crateApiNearNearTransactionHash({required List transaction}) { @@ -1206,10 +948,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(transaction, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 33)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiNearNearTransactionHashConstMeta, argValues: [transaction], apiImpl: this, @@ -1218,10 +957,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearNearTransactionHashConstMeta => - const TaskConstMeta( - debugName: 'near_transaction_hash', - argNames: ['transaction'], - ); + const TaskConstMeta(debugName: 'near_transaction_hash', argNames: ['transaction']); @override String crateApiMultisigPredictMultisigAddress({ @@ -1238,10 +974,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(nonce, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 34)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: sse_decode_String), constMeta: kCrateApiMultisigPredictMultisigAddressConstMeta, argValues: [signers, threshold, nonce], apiImpl: this, @@ -1250,10 +983,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiMultisigPredictMultisigAddressConstMeta => - const TaskConstMeta( - debugName: 'predict_multisig_address', - argNames: ['signers', 'threshold', 'nonce'], - ); + const TaskConstMeta(debugName: 'predict_multisig_address', argNames: ['signers', 'threshold', 'nonce']); @override Future crateApiAirdropProveAirdropWormhole({ @@ -1266,17 +996,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); sse_encode_list_prim_u_8_loose(wormholeSecret, serializer); sse_encode_String(claimAccount, serializer); - pdeCallFfi( - generalizedFrbRustBinding, - serializer, - funcId: 35, - port: port_, - ); + pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 35, port: port_); }, - codec: SseCodec( - decodeSuccessData: sse_decode_wormhole_claim_body, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_wormhole_claim_body, decodeErrorData: sse_decode_String), constMeta: kCrateApiAirdropProveAirdropWormholeConstMeta, argValues: [wormholeSecret, claimAccount], apiImpl: this, @@ -1285,10 +1007,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiAirdropProveAirdropWormholeConstMeta => - const TaskConstMeta( - debugName: 'prove_airdrop_wormhole', - argNames: ['wormholeSecret', 'claimAccount'], - ); + const TaskConstMeta(debugName: 'prove_airdrop_wormhole', argNames: ['wormholeSecret', 'claimAccount']); @override int crateApiCryptoPublicKeyBytes({required DilithiumScheme scheme}) { @@ -1299,10 +1018,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 36)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoPublicKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1322,10 +1038,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 37)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSecretKeyBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1345,10 +1058,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_16(prefix, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 38)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_unit, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_unit, decodeErrorData: null), constMeta: kCrateApiCryptoSetDefaultSs58PrefixConstMeta, argValues: [prefix], apiImpl: this, @@ -1357,10 +1067,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoSetDefaultSs58PrefixConstMeta => - const TaskConstMeta( - debugName: 'set_default_ss58_prefix', - argNames: ['prefix'], - ); + const TaskConstMeta(debugName: 'set_default_ss58_prefix', argNames: ['prefix']); @override Uint8List crateApiCryptoSignMessage({ @@ -1379,10 +1086,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 39)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -1390,10 +1094,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => const TaskConstMeta( - debugName: 'sign_message', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageConstMeta => + const TaskConstMeta(debugName: 'sign_message', argNames: ['keypair', 'message', 'entropy', 'specVersion']); @override Uint8List crateApiCryptoSignMessageWithPubkey({ @@ -1412,10 +1114,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 40)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: null), constMeta: kCrateApiCryptoSignMessageWithPubkeyConstMeta, argValues: [keypair, message, entropy, specVersion], apiImpl: this, @@ -1423,11 +1122,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } - TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => - const TaskConstMeta( - debugName: 'sign_message_with_pubkey', - argNames: ['keypair', 'message', 'entropy', 'specVersion'], - ); + TaskConstMeta get kCrateApiCryptoSignMessageWithPubkeyConstMeta => const TaskConstMeta( + debugName: 'sign_message_with_pubkey', + argNames: ['keypair', 'message', 'entropy', 'specVersion'], + ); @override Uint8List crateApiNearSignNearTransaction({ @@ -1444,10 +1142,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_opt_u_8_array_32(entropy, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 41)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiNearSignNearTransactionConstMeta, argValues: [keypair, transaction, entropy], apiImpl: this, @@ -1456,10 +1151,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearSignNearTransactionConstMeta => - const TaskConstMeta( - debugName: 'sign_near_transaction', - argNames: ['keypair', 'transaction', 'entropy'], - ); + const TaskConstMeta(debugName: 'sign_near_transaction', argNames: ['keypair', 'transaction', 'entropy']); @override int crateApiCryptoSignatureBytes({required DilithiumScheme scheme}) { @@ -1470,10 +1162,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_dilithium_scheme(scheme, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 42)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: null), constMeta: kCrateApiCryptoSignatureBytesConstMeta, argValues: [scheme], apiImpl: this, @@ -1493,10 +1182,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_String(s, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 43)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_list_prim_u_8_strict, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_list_prim_u_8_strict, decodeErrorData: sse_decode_String), constMeta: kCrateApiCryptoSs58ToAccountIdConstMeta, argValues: [s], apiImpl: this, @@ -1516,10 +1202,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_box_autoadd_keypair(obj, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 44)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiCryptoToAccountIdConstMeta, argValues: [obj], apiImpl: this, @@ -1547,10 +1230,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(specVersion, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 45)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_bool, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiCryptoVerifyMessageConstMeta, argValues: [keypair, message, signature, specVersion], apiImpl: this, @@ -1559,10 +1239,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiCryptoVerifyMessageConstMeta => - const TaskConstMeta( - debugName: 'verify_message', - argNames: ['keypair', 'message', 'signature', 'specVersion'], - ); + const TaskConstMeta(debugName: 'verify_message', argNames: ['keypair', 'message', 'signature', 'specVersion']); @override bool crateApiNearVerifyNearSignature({ @@ -1579,10 +1256,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_list_prim_u_8_loose(signature, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 46)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_bool, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_bool, decodeErrorData: null), constMeta: kCrateApiNearVerifyNearSignatureConstMeta, argValues: [publicKey, transaction, signature], apiImpl: this, @@ -1591,16 +1265,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiNearVerifyNearSignatureConstMeta => - const TaskConstMeta( - debugName: 'verify_near_signature', - argNames: ['publicKey', 'transaction', 'signature'], - ); + const TaskConstMeta(debugName: 'verify_near_signature', argNames: ['publicKey', 'transaction', 'signature']); @override - int crateApiWormholeWormholeComputeOutputAmount({ - required int inputAmount, - required int feeBps, - }) { + int crateApiWormholeWormholeComputeOutputAmount({required int inputAmount, required int feeBps}) { return handler.executeSync( SyncTask( callFfi: () { @@ -1609,10 +1277,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_32(feeBps, serializer); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 47)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_u_32, - decodeErrorData: sse_decode_String, - ), + codec: SseCodec(decodeSuccessData: sse_decode_u_32, decodeErrorData: sse_decode_String), constMeta: kCrateApiWormholeWormholeComputeOutputAmountConstMeta, argValues: [inputAmount, feeBps], apiImpl: this, @@ -1621,10 +1286,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } TaskConstMeta get kCrateApiWormholeWormholeComputeOutputAmountConstMeta => - const TaskConstMeta( - debugName: 'wormhole_compute_output_amount', - argNames: ['inputAmount', 'feeBps'], - ); + const TaskConstMeta(debugName: 'wormhole_compute_output_amount', argNames: ['inputAmount', 'feeBps']); @override String crateApiWormholeZkCircuitsVersion() { @@ -1634,10 +1296,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { final serializer = SseSerializer(generalizedFrbRustBinding); return pdeCallFfi(generalizedFrbRustBinding, serializer, funcId: 48)!; }, - codec: SseCodec( - decodeSuccessData: sse_decode_String, - decodeErrorData: null, - ), + codec: SseCodec(decodeSuccessData: sse_decode_String, decodeErrorData: null), constMeta: kCrateApiWormholeZkCircuitsVersionConstMeta, argValues: [], apiImpl: this, @@ -1648,17 +1307,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { TaskConstMeta get kCrateApiWormholeZkCircuitsVersionConstMeta => const TaskConstMeta(debugName: 'zk_circuits_version', argNames: []); - RustArcIncrementStrongCountFnType - get rust_arc_increment_strong_count_HdLatticeError => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_HdLatticeError => + wire.rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; - RustArcDecrementStrongCountFnType - get rust_arc_decrement_strong_count_HdLatticeError => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_HdLatticeError => + wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -1666,10 +1322,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ) { + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return HdLatticeErrorImpl.frbInternalDcoDecode(raw as List); } @@ -1690,8 +1343,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { AirdropMatch dco_decode_airdrop_match(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 7) - throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); + if (arr.length != 7) throw Exception('unexpected arr length: expect 7 but see ${arr.length}'); return AirdropMatch( address: dco_decode_String(arr[0]), kind: dco_decode_String(arr[1]), @@ -1737,8 +1389,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { DilithiumClaimBody dco_decode_dilithium_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) - throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); return DilithiumClaimBody( scheme: dco_decode_String(arr[0]), address: dco_decode_String(arr[1]), @@ -1771,8 +1422,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { Keypair dco_decode_keypair(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return Keypair( publicKey: dco_decode_list_prim_u_8_strict(arr[0]), secretKey: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1820,8 +1470,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { MerkleProcessed dco_decode_merkle_processed(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return MerkleProcessed( sortedSiblingsFlat: dco_decode_list_prim_u_8_strict(arr[0]), positions: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1832,8 +1481,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NearAction dco_decode_near_action(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 9) - throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); + if (arr.length != 9) throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); return NearAction( kind: dco_decode_near_action_kind(arr[0]), target: dco_decode_opt_String(arr[1]), @@ -1857,8 +1505,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NearTransaction dco_decode_near_transaction(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 9) - throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); + if (arr.length != 9) throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); return NearTransaction( signerId: dco_decode_String(arr[0]), publicKey: dco_decode_String(arr[1]), @@ -1912,8 +1559,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofInput dco_decode_proof_input(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 19) - throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); + if (arr.length != 19) throw Exception('unexpected arr length: expect 19 but see ${arr.length}'); return ProofInput( secret: dco_decode_list_prim_u_8_strict(arr[0]), transferCount: dco_decode_u_64(arr[1]), @@ -1941,8 +1587,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ProofOutput dco_decode_proof_output(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return ProofOutput( proofBytes: dco_decode_list_prim_u_8_strict(arr[0]), nullifier: dco_decode_list_prim_u_8_strict(arr[1]), @@ -1995,20 +1640,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { WormholeClaimBody dco_decode_wormhole_claim_body(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 2) - throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); - return WormholeClaimBody( - proofKind: dco_decode_String(arr[0]), - proofHex: dco_decode_String(arr[1]), - ); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return WormholeClaimBody(proofKind: dco_decode_String(arr[0]), proofHex: dco_decode_String(arr[1])); } @protected WormholeResult dco_decode_wormhole_result(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) - throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); return WormholeResult( address: dco_decode_String(arr[0]), firstHash: dco_decode_list_prim_u_8_strict(arr[1]), @@ -2017,27 +1657,19 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - return HdLatticeErrorImpl.frbInternalSseDecode( - sse_decode_usize(deserializer), - sse_decode_i_32(deserializer), - ); + return HdLatticeErrorImpl.frbInternalSseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); } @protected @@ -2106,9 +1738,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ) { + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_scheme = sse_decode_String(deserializer); var var_address = sse_decode_String(deserializer); @@ -2151,11 +1781,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_publicKey = sse_decode_list_prim_u_8_strict(deserializer); var var_secretKey = sse_decode_list_prim_u_8_strict(deserializer); var var_scheme = sse_decode_dilithium_scheme(deserializer); - return Keypair( - publicKey: var_publicKey, - secretKey: var_secretKey, - scheme: var_scheme, - ); + return Keypair(publicKey: var_publicKey, secretKey: var_secretKey, scheme: var_scheme); } @protected @@ -2171,9 +1797,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ) { + List sse_decode_list_airdrop_match(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -2185,9 +1809,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ) { + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var len_ = sse_decode_i_32(deserializer); @@ -2229,10 +1851,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs var var_sortedSiblingsFlat = sse_decode_list_prim_u_8_strict(deserializer); var var_positions = sse_decode_list_prim_u_8_strict(deserializer); - return MerkleProcessed( - sortedSiblingsFlat: var_sortedSiblingsFlat, - positions: var_positions, - ); + return MerkleProcessed(sortedSiblingsFlat: var_sortedSiblingsFlat, positions: var_positions); } @protected @@ -2454,9 +2073,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ) { + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_proofKind = sse_decode_String(deserializer); var var_proofHex = sse_decode_String(deserializer); @@ -2469,37 +2086,25 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_address = sse_decode_String(deserializer); var var_firstHash = sse_decode_list_prim_u_8_strict(deserializer); var var_secret = sse_decode_list_prim_u_8_strict(deserializer); - return WormholeResult( - address: var_address, - firstHash: var_firstHash, - secret: var_secret, - ); + return WormholeResult(address: var_address, firstHash: var_firstHash, secret: var_secret); } @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), - serializer, - ); + sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: true), serializer); } @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize( - (self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), - serializer, - ); + sse_encode_usize((self as HdLatticeErrorImpl).frbInternalSseEncode(move: null), serializer); } @protected @@ -2539,10 +2144,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ) { + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_proof_input(self, serializer); } @@ -2560,10 +2162,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ) { + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.scheme, serializer); sse_encode_String(self.address, serializer); @@ -2574,10 +2173,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ) { + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.index, serializer); } @@ -2612,10 +2208,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_airdrop_match(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -2624,10 +2217,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -2636,10 +2226,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_near_action( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_near_action(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); for (final item in self) { @@ -2648,32 +2235,21 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_list_prim_u_8_loose( - List self, - SseSerializer serializer, - ) { + void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); - serializer.buffer.putUint8List( - self is Uint8List ? self : Uint8List.fromList(self), - ); + serializer.buffer.putUint8List(self is Uint8List ? self : Uint8List.fromList(self)); } @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ) { + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.length, serializer); serializer.buffer.putUint8List(self); } @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ) { + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_list_prim_u_8_strict(self.sortedSiblingsFlat, serializer); sse_encode_list_prim_u_8_strict(self.positions, serializer); @@ -2694,19 +2270,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_near_action_kind( - NearActionKind self, - SseSerializer serializer, - ) { + void sse_encode_near_action_kind(NearActionKind self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_i_32(self.index, serializer); } @protected - void sse_encode_near_transaction( - NearTransaction self, - SseSerializer serializer, - ) { + void sse_encode_near_transaction(NearTransaction self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.signerId, serializer); sse_encode_String(self.publicKey, serializer); @@ -2760,10 +2330,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ) { + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_bool(self != null, serializer); @@ -2855,20 +2422,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ) { + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.proofKind, serializer); sse_encode_String(self.proofHex, serializer); } @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ) { + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_String(self.address, serializer); sse_encode_list_prim_u_8_strict(self.firstHash, serializer); @@ -2879,19 +2440,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @sealed class HdLatticeErrorImpl extends RustOpaque implements HdLatticeError { // Not to be used by end users - HdLatticeErrorImpl.frbInternalDcoDecode(List wire) - : super.frbInternalDcoDecode(wire, _kStaticData); + HdLatticeErrorImpl.frbInternalDcoDecode(List wire) : super.frbInternalDcoDecode(wire, _kStaticData); // Not to be used by end users HdLatticeErrorImpl.frbInternalSseDecode(BigInt ptr, int externalSizeOnNative) : super.frbInternalSseDecode(ptr, externalSizeOnNative, _kStaticData); static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: - RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, - rustArcDecrementStrongCount: - RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, - rustArcDecrementStrongCountPtr: - RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, + rustArcIncrementStrongCount: RustLib.instance.api.rust_arc_increment_strong_count_HdLatticeError, + rustArcDecrementStrongCount: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeError, + rustArcDecrementStrongCountPtr: RustLib.instance.api.rust_arc_decrement_strong_count_HdLatticeErrorPtr, ); } diff --git a/quantus_sdk/lib/src/rust/frb_generated.io.dart b/quantus_sdk/lib/src/rust/frb_generated.io.dart index 67004831b..6c519da61 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.io.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.io.dart @@ -24,21 +24,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ); + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -161,14 +156,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @@ -197,9 +190,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ); + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -217,14 +208,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ); + List sse_decode_list_airdrop_match(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ); + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); @protected List sse_decode_list_near_action(SseDeserializer deserializer); @@ -293,23 +280,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ); + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -330,10 +313,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ); + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @@ -342,16 +322,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ); + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ); + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -366,52 +340,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ); + void sse_encode_list_airdrop_match(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ); + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); @protected - void sse_encode_list_near_action( - List self, - SseSerializer serializer, - ); + void sse_encode_list_near_action(List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ); + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); @protected void sse_encode_near_action(NearAction self, SseSerializer serializer); @protected - void sse_encode_near_action_kind( - NearActionKind self, - SseSerializer serializer, - ); + void sse_encode_near_action_kind(NearActionKind self, SseSerializer serializer); @protected - void sse_encode_near_transaction( - NearTransaction self, - SseSerializer serializer, - ); + void sse_encode_near_transaction(NearTransaction self, SseSerializer serializer); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); @@ -426,10 +379,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ); + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -462,34 +412,24 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ); + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ); + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); } // Section: wire_class class RustLibWire implements BaseWire { - factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => - RustLibWire(lib.ffiDynamicLibrary); + factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) => RustLibWire(lib.ffiDynamicLibrary); /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. - RustLibWire(ffi.DynamicLibrary dynamicLibrary) - : _lookup = dynamicLibrary.lookup; + RustLibWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -505,8 +445,7 @@ class RustLibWire implements BaseWire { _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeErrorPtr .asFunction)>(); - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ffi.Pointer ptr, ) { return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( diff --git a/quantus_sdk/lib/src/rust/frb_generated.web.dart b/quantus_sdk/lib/src/rust/frb_generated.web.dart index 5b7e6d217..64b26bfb5 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.web.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.web.dart @@ -26,21 +26,16 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg - get rust_arc_decrement_strong_count_HdLatticeErrorPtr => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; + CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_HdLatticeErrorPtr => + wire.rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError; @protected - HdLatticeError - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( dynamic raw, ); @protected - HdLatticeError - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - dynamic raw, - ); + HdLatticeError dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(dynamic raw); @protected String dco_decode_String(dynamic raw); @@ -163,14 +158,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { WormholeResult dco_decode_wormhole_result(dynamic raw); @protected - HdLatticeError - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @protected - HdLatticeError - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + HdLatticeError sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( SseDeserializer deserializer, ); @@ -199,9 +192,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_box_autoadd_u_64(SseDeserializer deserializer); @protected - DilithiumClaimBody sse_decode_dilithium_claim_body( - SseDeserializer deserializer, - ); + DilithiumClaimBody sse_decode_dilithium_claim_body(SseDeserializer deserializer); @protected DilithiumScheme sse_decode_dilithium_scheme(SseDeserializer deserializer); @@ -219,14 +210,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { List sse_decode_list_String(SseDeserializer deserializer); @protected - List sse_decode_list_airdrop_match( - SseDeserializer deserializer, - ); + List sse_decode_list_airdrop_match(SseDeserializer deserializer); @protected - List sse_decode_list_list_prim_u_8_strict( - SseDeserializer deserializer, - ); + List sse_decode_list_list_prim_u_8_strict(SseDeserializer deserializer); @protected List sse_decode_list_near_action(SseDeserializer deserializer); @@ -295,23 +282,19 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BigInt sse_decode_usize(SseDeserializer deserializer); @protected - WormholeClaimBody sse_decode_wormhole_claim_body( - SseDeserializer deserializer, - ); + WormholeClaimBody sse_decode_wormhole_claim_body(SseDeserializer deserializer); @protected WormholeResult sse_decode_wormhole_result(SseDeserializer deserializer); @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @protected - void - sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( HdLatticeError self, SseSerializer serializer, ); @@ -332,10 +315,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_keypair(Keypair self, SseSerializer serializer); @protected - void sse_encode_box_autoadd_proof_input( - ProofInput self, - SseSerializer serializer, - ); + void sse_encode_box_autoadd_proof_input(ProofInput self, SseSerializer serializer); @protected void sse_encode_box_autoadd_u_32(int self, SseSerializer serializer); @@ -344,16 +324,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_box_autoadd_u_64(BigInt self, SseSerializer serializer); @protected - void sse_encode_dilithium_claim_body( - DilithiumClaimBody self, - SseSerializer serializer, - ); + void sse_encode_dilithium_claim_body(DilithiumClaimBody self, SseSerializer serializer); @protected - void sse_encode_dilithium_scheme( - DilithiumScheme self, - SseSerializer serializer, - ); + void sse_encode_dilithium_scheme(DilithiumScheme self, SseSerializer serializer); @protected void sse_encode_i_32(int self, SseSerializer serializer); @@ -368,52 +342,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_list_String(List self, SseSerializer serializer); @protected - void sse_encode_list_airdrop_match( - List self, - SseSerializer serializer, - ); + void sse_encode_list_airdrop_match(List self, SseSerializer serializer); @protected - void sse_encode_list_list_prim_u_8_strict( - List self, - SseSerializer serializer, - ); + void sse_encode_list_list_prim_u_8_strict(List self, SseSerializer serializer); @protected - void sse_encode_list_near_action( - List self, - SseSerializer serializer, - ); + void sse_encode_list_near_action(List self, SseSerializer serializer); @protected void sse_encode_list_prim_u_8_loose(List self, SseSerializer serializer); @protected - void sse_encode_list_prim_u_8_strict( - Uint8List self, - SseSerializer serializer, - ); + void sse_encode_list_prim_u_8_strict(Uint8List self, SseSerializer serializer); @protected - void sse_encode_merkle_processed( - MerkleProcessed self, - SseSerializer serializer, - ); + void sse_encode_merkle_processed(MerkleProcessed self, SseSerializer serializer); @protected void sse_encode_near_action(NearAction self, SseSerializer serializer); @protected - void sse_encode_near_action_kind( - NearActionKind self, - SseSerializer serializer, - ); + void sse_encode_near_action_kind(NearActionKind self, SseSerializer serializer); @protected - void sse_encode_near_transaction( - NearTransaction self, - SseSerializer serializer, - ); + void sse_encode_near_transaction(NearTransaction self, SseSerializer serializer); @protected void sse_encode_opt_String(String? self, SseSerializer serializer); @@ -428,10 +381,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_opt_box_autoadd_u_64(BigInt? self, SseSerializer serializer); @protected - void sse_encode_opt_list_prim_u_8_strict( - Uint8List? self, - SseSerializer serializer, - ); + void sse_encode_opt_list_prim_u_8_strict(Uint8List? self, SseSerializer serializer); @protected void sse_encode_opt_u_8_array_32(U8Array32? self, SseSerializer serializer); @@ -464,16 +414,10 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_usize(BigInt self, SseSerializer serializer); @protected - void sse_encode_wormhole_claim_body( - WormholeClaimBody self, - SseSerializer serializer, - ); + void sse_encode_wormhole_claim_body(WormholeClaimBody self, SseSerializer serializer); @protected - void sse_encode_wormhole_result( - WormholeResult self, - SseSerializer serializer, - ); + void sse_encode_wormhole_result(WormholeResult self, SseSerializer serializer); } // Section: wire_class @@ -481,16 +425,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { class RustLibWire implements BaseWire { RustLibWire.fromExternalLibrary(ExternalLibrary lib); - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( ptr, ); - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( + void rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( int ptr, ) => wasmModule .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( @@ -505,12 +447,8 @@ external RustLibWasmModule get wasmModule; @anonymous extension type RustLibWasmModule._(JSObject _) implements JSObject { external void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - int ptr, - ); + rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); external void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError( - int ptr, - ); + rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerHDLatticeError(int ptr); } From aa2635235aaf57d067acbb566c7de3ba204175eb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:10:47 +0000 Subject: [PATCH 05/16] sdk: refuse NEAR account ids the chain would refuse decode_near_transaction checks signer, receiver, beneficiary and function-call-key receiver ids against NEAR's rules, which also keeps every account id shown to a signer plain ASCII. --- quantus_sdk/rust/src/api/near.rs | 81 ++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/quantus_sdk/rust/src/api/near.rs b/quantus_sdk/rust/src/api/near.rs index 4cdb47518..b86064a43 100644 --- a/quantus_sdk/rust/src/api/near.rs +++ b/quantus_sdk/rust/src/api/near.rs @@ -130,11 +130,12 @@ struct WireTransaction { /// The kind of a [`NearAction`]. Everything but `Transfer` and /// `FunctionCall` changes who controls the account or what code it runs. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum NearActionKind { CreateAccount, DeployContract, FunctionCall, + #[default] Transfer, Stake, AddKey, @@ -171,12 +172,6 @@ pub struct NearAction { pub code_len: Option, } -impl Default for NearActionKind { - fn default() -> Self { - NearActionKind::Transfer - } -} - impl From<&WireAction> for NearAction { fn from(action: &WireAction) -> Self { match action { @@ -269,7 +264,48 @@ pub struct NearTransaction { } fn decode_wire(bytes: &[u8]) -> Result { - borsh::from_slice(bytes).map_err(|e| format!("Not a borsh NEAR transaction: {e}")) + let wire: WireTransaction = + borsh::from_slice(bytes).map_err(|e| format!("Not a borsh NEAR transaction: {e}"))?; + check_account_id("signer", &wire.signer_id)?; + check_account_id("receiver", &wire.receiver_id)?; + for action in &wire.actions { + match action { + WireAction::DeleteAccount { beneficiary_id } => { + check_account_id("beneficiary", beneficiary_id)? + } + WireAction::AddKey { access_key, .. } => { + if let WireAccessKeyPermission::FunctionCall(p) = &access_key.permission { + check_account_id("key receiver", &p.receiver_id)? + } + } + _ => {} + } + } + Ok(wire) +} + +/// NEAR's account id rules: 2–64 chars of `a-z 0-9 _ - .`, separators never +/// doubled or at either end of a part. The chain refuses anything else, and +/// the check also keeps every account id shown to a signer plain ASCII. +fn check_account_id(role: &str, id: &str) -> Result<(), String> { + let valid = (2..=64).contains(&id.len()) + && id.split('.').all(|part| { + !part.is_empty() + && !part.starts_with(['-', '_']) + && !part.ends_with(['-', '_']) + && !part.contains("--") + && !part.contains("__") + && !part.contains("-_") + && !part.contains("_-") + && part + .bytes() + .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'-' || b == b'_') + }); + if valid { + Ok(()) + } else { + Err(format!("Not a NEAR account id ({role}): {id:?}")) + } } /// Decode a borsh `TransactionV0`. Refuses trailing bytes. @@ -467,6 +503,35 @@ mod tests { assert_eq!(near_transaction_hash(bytes), tx.hash); } + #[test] + fn rejects_account_ids_the_chain_would_refuse() { + for good in ["alice.testnet", "a1", "sub.a-b_c.near", &"a".repeat(64)] { + assert!(check_account_id("t", good).is_ok(), "{good}"); + } + for bad in [ + "a", + "Alice.testnet", + "alice..testnet", + ".alice", + "alice.", + "-alice", + "alice-", + "ali--ce", + "ali_-ce", + "alice\u{202E}testnet", + "alice testnet", + &"a".repeat(65), + ] { + assert!(check_account_id("t", bad).is_err(), "{bad:?}"); + } + + let mut bytes = b64(NEAR_CLI_UNSIGNED_B64); + // Flip the first byte of "alice.testnet" to an uppercase 'A'. + bytes[4] = b'A'; + let err = decode_near_transaction(bytes).unwrap_err(); + assert!(err.contains("signer"), "{err}"); + } + #[test] fn rejects_trailing_and_truncated_bytes() { let bytes = b64(NEAR_CLI_UNSIGNED_B64); From 395ba9800acc7858bd85c79dc8d21fdf2f372ec4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:10:47 +0000 Subject: [PATCH 06/16] cold-wallet: show NEAR method names and arguments as hex unless display-safe Control, bidi and zero-width format characters in a prepared transaction's strings could reorder or hide what the review shows; such text falls back to its bytes. --- .../screens/sign_near_transaction_screen.dart | 4 ++-- .../lib/services/near_display.dart | 21 ++++++++++++++++++- cold-wallet-app/test/near_display_test.dart | 12 +++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index 1b70d6a42..efb62bb3e 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -332,7 +332,7 @@ class _SignNearTransactionScreenState extends ConsumerState [DetailSummaryRow.stacked(label: 'Amount', value: near(action.amount))], NearActionKind.functionCall => [ - DetailSummaryRow.stacked(label: 'Method', value: action.target ?? '', monospace: true), + DetailSummaryRow.stacked(label: 'Method', value: NearDisplay.safeText(action.target ?? ''), monospace: true), DetailSummaryRow.stacked(label: 'Attached deposit', value: near(action.amount)), DetailSummaryRow.stacked(label: 'Gas', value: NearDisplay.formatGas(action.gas ?? BigInt.zero)), DetailSummaryRow.stacked( @@ -369,7 +369,7 @@ class _SignNearTransactionScreenState extends ConsumerState isDisplaySafe(text) ? text : '0x${hex.encode(utf8.encode(text))}'; + + /// Whether every character renders in place: no control characters, and + /// none of the bidi or zero-width format characters that could reverse, + /// join or hide the text around them. + static bool isDisplaySafe(String text, {bool allowNewlines = false}) => text.runes.every((r) { + if (allowNewlines && r == 0x0A) return true; + if (r < 0x20 || (r >= 0x7F && r <= 0x9F)) return false; + return !((r >= 0x200B && r <= 0x200F) || + (r >= 0x202A && r <= 0x202E) || + (r >= 0x2060 && r <= 0x2064) || + (r >= 0x2066 && r <= 0x2069) || + r == 0xFEFF); + }); + static String actionTitle(NearAction action) => switch (action.kind) { NearActionKind.transfer => 'SEND', NearActionKind.functionCall => 'CALL CONTRACT', diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index 492fd0870..c0f3bd480 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -49,6 +49,18 @@ void main() { expect(NearDisplay.argsText(Uint8List(0)), '(none)'); }); + test('text that could reorder or hide what is shown falls back to hex', () { + expect(NearDisplay.safeText('ft_transfer'), 'ft_transfer'); + expect(NearDisplay.safeText('a\u202Eb'), '0x61e280ae62'); + expect(NearDisplay.safeText('a\u200Bb'), '0x61e2808b62'); + expect(NearDisplay.safeText('a\x1bb'), '0x611b62'); + // JSON strings carrying a bidi override are shown as the raw bytes. + final args = Uint8List.fromList(utf8.encode('{"to":"bob\u202Eten.tset"}')); + expect(NearDisplay.argsText(args), '0x${args.map((b) => b.toRadixString(16).padLeft(2, '0')).join()}'); + // Pretty-printed JSON keeps its newlines; escaped controls inside strings stay escaped. + expect(NearDisplay.argsText(Uint8List.fromList(utf8.encode('{"a":"x\\ny"}'))), '{\n "a": "x\\ny"\n}'); + }); + test('headline names a single action and counts several', () { expect(NearDisplay.headline(_tx(actions: [_action(NearActionKind.transfer)])), 'SEND'); expect( From dd21c9732c73d8df7bf80108a54838c0c93fe3a5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:32:33 +0000 Subject: [PATCH 07/16] near: accept only the exact mainnet/testnet labels in a signing request The label is what the signer is told and what selects the account-name check, so a lookalike such as "testnet " could read as testnet while disabling the testnet check. NearSigningRequest.decode now refuses anything but the supported names, and NearDisplay.networkMismatch treats any other label as a programming error rather than a pass. --- .../lib/services/near_display.dart | 7 +++--- cold-wallet-app/test/near_display_test.dart | 3 ++- .../sign_near_transaction_screen_test.dart | 11 +++++++++ .../lib/src/models/signing_request.dart | 9 +++++-- .../test/models/signing_request_test.dart | 24 +++++++++++++++++++ 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/cold-wallet-app/lib/services/near_display.dart b/cold-wallet-app/lib/services/near_display.dart index b50750534..dbd8eeb89 100644 --- a/cold-wallet-app/lib/services/near_display.dart +++ b/cold-wallet-app/lib/services/near_display.dart @@ -85,14 +85,15 @@ class NearDisplay { /// The envelope's network is only a label the hot wallet attached; the /// account names are what the chain will see. Named accounts end in the - /// network's top-level name, so a mismatch means one side is wrong. + /// network's top-level name, so a mismatch means one side is wrong. Only + /// the labels [NearSigningRequest.supportedNetworks] admits reach here; + /// anything else is a bug upstream and is reported, never passed over. static String? networkMismatch(NearTransaction tx, String network) { final tld = switch (network) { 'mainnet' => '.near', 'testnet' => '.testnet', - _ => null, + _ => throw ArgumentError.value(network, 'network', 'not a supported NEAR network'), }; - if (tld == null) return null; final mismatched = [tx.signerId, tx.receiverId].where((id) => _isNamed(id) && !id.endsWith(tld)).toList(); if (mismatched.isEmpty) return null; return 'This request says $network, but ${mismatched.join(' and ')} ' diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index c0f3bd480..cfd599cb5 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -94,7 +94,8 @@ void main() { expect(NearDisplay.networkMismatch(_tx(), 'testnet'), isNull); expect(NearDisplay.networkMismatch(_tx(signer: 'alice.near', receiver: 'bob.near'), 'mainnet'), isNull); expect(NearDisplay.networkMismatch(_tx(signer: 'a' * 64, receiver: '0x${'b' * 40}'), 'mainnet'), isNull); - expect(NearDisplay.networkMismatch(_tx(), 'localnet'), isNull); + expect(() => NearDisplay.networkMismatch(_tx(), 'localnet'), throwsArgumentError); + expect(() => NearDisplay.networkMismatch(_tx(), 'testnet '), throwsArgumentError); }); test('names each account that does not belong to the network', () { diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart index 53744f6c9..b7c4e91c3 100644 --- a/cold-wallet-app/test/sign_near_transaction_screen_test.dart +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -154,6 +154,17 @@ void main() { expect(find.text('Sign'), findsOneWidget); }); + testWidgets('a mainnet transfer labelled mainnet is reviewed without a mismatch warning', (tester) async { + await pump( + tester, + _tx(signer: 'alice.near', receiver: 'bob.near', actions: [_transfer(_near)]), + network: 'mainnet', + ); + + expect(find.text('NEAR · MAINNET'), findsOneWidget); + expect(find.text('Network does not match'), findsNothing); + }); + testWidgets('refuses a key no account here holds, and offers no Sign', (tester) async { await pump(tester, _tx(actions: [_transfer(_near)]), keyOwner: null); diff --git a/quantus_sdk/lib/src/models/signing_request.dart b/quantus_sdk/lib/src/models/signing_request.dart index 618d142b6..2c5c41843 100644 --- a/quantus_sdk/lib/src/models/signing_request.dart +++ b/quantus_sdk/lib/src/models/signing_request.dart @@ -90,6 +90,11 @@ class NearSigningRequest extends AnySigningRequest { static const int version = 2; static const String chain = 'near'; + /// The only network labels a request may carry, spelled exactly. The label + /// drives what the signer is told and which account-name check runs, so a + /// lookalike such as `"testnet "` must be refused rather than shown. + static const Set supportedNetworks = {'mainnet', 'testnet'}; + final String network; final Uint8List transaction; @@ -109,8 +114,8 @@ class NearSigningRequest extends AnySigningRequest { if (json['chain'] != chain) throw FormatException('Signing request is for chain ${json['chain']}, not NEAR'); final network = json['network']; - if (network is! String || network.isEmpty) { - throw const FormatException('Signing request names no NEAR network'); + if (network is! String || !supportedNetworks.contains(network)) { + throw FormatException('Signing request NEAR network ${jsonEncode(network)} is not one of $supportedNetworks'); } return NearSigningRequest(network: network, transaction: _decodePayload(json['payload'])); diff --git a/quantus_sdk/test/models/signing_request_test.dart b/quantus_sdk/test/models/signing_request_test.dart index 6197d0b51..1b6a65d4b 100644 --- a/quantus_sdk/test/models/signing_request_test.dart +++ b/quantus_sdk/test/models/signing_request_test.dart @@ -121,5 +121,29 @@ void main() { expect(() => NearSigningRequest.decode(envelope({...nearJson, 'payload': '0x'})), throwsFormatException); expect(() => NearSigningRequest.decode(envelope({...nearJson, 'payload': 'abcd'})), throwsFormatException); }); + + test('accepts only the exact supported network labels, never a lookalike', () { + for (final network in NearSigningRequest.supportedNetworks) { + expect(NearSigningRequest.decode(envelope({...nearJson, 'network': network})).network, network); + } + // A trailing space, different case, or a zero-width character would + // read as "testnet" on screen while disabling the testnet checks. + for (final lookalike in [ + 'testnet ', + ' testnet', + 'Testnet', + 'TESTNET', + 'test\u200Bnet', + 'mainnet\n', + 'localnet', + ]) { + expect( + () => NearSigningRequest.decode(envelope({...nearJson, 'network': lookalike})), + throwsFormatException, + reason: jsonEncode(lookalike), + ); + } + expect(() => NearSigningRequest.decode(envelope({...nearJson, 'network': 1})), throwsFormatException); + }); }); } From f7756c9f6e545499d79b85704aa36facc7fd48c2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 07:47:28 +0000 Subject: [PATCH 08/16] near: identify deployed contract code by its SHA-256 in the review Two WASM blobs of the same length produced the same review; the digest is how NEAR identifies code, so a signer can compare it with the artifact they meant to deploy. --- .../screens/sign_near_transaction_screen.dart | 8 ++++--- .../sign_near_transaction_screen_test.dart | 23 +++++++++++++++++++ quantus_sdk/lib/src/rust/api/near.dart | 11 +++++++-- quantus_sdk/lib/src/rust/frb_generated.dart | 6 ++++- quantus_sdk/rust/src/api/near.rs | 8 +++++++ quantus_sdk/rust/src/frb_generated.rs | 4 ++++ 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index efb62bb3e..80b31f115 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -346,10 +346,12 @@ class _SignNearTransactionScreenState extends ConsumerState [ DetailSummaryRow.stacked(label: 'Deploys to', value: tx.receiverId, monospace: true), + DetailSummaryRow.stacked(label: 'Code size', value: '${action.codeLen ?? 0} bytes'), DetailSummaryRow.stacked( - label: 'Code size', - value: '${action.codeLen ?? 0} bytes', - note: 'The code itself cannot be reviewed here.', + label: 'Code SHA-256', + value: hex.encode(action.codeHash ?? Uint8List(0)), + monospace: true, + note: 'Compare with the hash of the contract you meant to deploy; the code itself cannot be shown here.', ), ], NearActionKind.stake => [ diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart index b7c4e91c3..d24a86d54 100644 --- a/cold-wallet-app/test/sign_near_transaction_screen_test.dart +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -115,6 +115,29 @@ void main() { expect(find.text('Sign'), findsOneWidget); }); + testWidgets('a contract deployment identifies the code by its hash, not just its size', (tester) async { + final hash = Uint8List.fromList(List.generate(32, (i) => i)); + await pump( + tester, + _tx( + receiver: 'alice.testnet', + actions: [ + NearAction( + kind: NearActionKind.deployContract, + codeLen: 4096, + codeHash: hash, + fullAccess: false, + methodNames: const [], + ), + ], + ), + ); + + expect(tester.widget(find.text('DEPLOY CONTRACT')).style?.color, colors.semanticEmber); + expect(find.text('4096 bytes'), findsOneWidget); + expect(find.text('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'), findsOneWidget); + }); + testWidgets('every action of a multi-action call is listed with its own parameters', (tester) async { await pump( tester, diff --git a/quantus_sdk/lib/src/rust/api/near.dart b/quantus_sdk/lib/src/rust/api/near.dart index 9cb60a215..73e3f17dd 100644 --- a/quantus_sdk/lib/src/rust/api/near.dart +++ b/quantus_sdk/lib/src/rust/api/near.dart @@ -83,6 +83,10 @@ class NearAction { /// `DeployContract`: size of the code blob. final int? codeLen; + /// `DeployContract`: SHA-256 of the code, how NEAR identifies contract + /// code, so a signer can compare it with the artifact they meant to deploy. + final Uint8List? codeHash; + const NearAction({ required this.kind, this.target, @@ -93,6 +97,7 @@ class NearAction { required this.fullAccess, required this.methodNames, this.codeLen, + this.codeHash, }); static Future default_() => RustLib.instance.api.crateApiNearNearActionDefault(); @@ -107,7 +112,8 @@ class NearAction { publicKey.hashCode ^ fullAccess.hashCode ^ methodNames.hashCode ^ - codeLen.hashCode; + codeLen.hashCode ^ + codeHash.hashCode; @override bool operator ==(Object other) => @@ -122,7 +128,8 @@ class NearAction { publicKey == other.publicKey && fullAccess == other.fullAccess && methodNames == other.methodNames && - codeLen == other.codeLen; + codeLen == other.codeLen && + codeHash == other.codeHash; } /// The kind of a [`NearAction`]. Everything but `Transfer` and diff --git a/quantus_sdk/lib/src/rust/frb_generated.dart b/quantus_sdk/lib/src/rust/frb_generated.dart index 866eabd6d..53eb01eb1 100644 --- a/quantus_sdk/lib/src/rust/frb_generated.dart +++ b/quantus_sdk/lib/src/rust/frb_generated.dart @@ -1481,7 +1481,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { NearAction dco_decode_near_action(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 9) throw Exception('unexpected arr length: expect 9 but see ${arr.length}'); + if (arr.length != 10) throw Exception('unexpected arr length: expect 10 but see ${arr.length}'); return NearAction( kind: dco_decode_near_action_kind(arr[0]), target: dco_decode_opt_String(arr[1]), @@ -1492,6 +1492,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { fullAccess: dco_decode_bool(arr[6]), methodNames: dco_decode_list_String(arr[7]), codeLen: dco_decode_opt_box_autoadd_u_32(arr[8]), + codeHash: dco_decode_opt_list_prim_u_8_strict(arr[9]), ); } @@ -1866,6 +1867,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var var_fullAccess = sse_decode_bool(deserializer); var var_methodNames = sse_decode_list_String(deserializer); var var_codeLen = sse_decode_opt_box_autoadd_u_32(deserializer); + var var_codeHash = sse_decode_opt_list_prim_u_8_strict(deserializer); return NearAction( kind: var_kind, target: var_target, @@ -1876,6 +1878,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { fullAccess: var_fullAccess, methodNames: var_methodNames, codeLen: var_codeLen, + codeHash: var_codeHash, ); } @@ -2267,6 +2270,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_bool(self.fullAccess, serializer); sse_encode_list_String(self.methodNames, serializer); sse_encode_opt_box_autoadd_u_32(self.codeLen, serializer); + sse_encode_opt_list_prim_u_8_strict(self.codeHash, serializer); } @protected diff --git a/quantus_sdk/rust/src/api/near.rs b/quantus_sdk/rust/src/api/near.rs index b86064a43..ca2297630 100644 --- a/quantus_sdk/rust/src/api/near.rs +++ b/quantus_sdk/rust/src/api/near.rs @@ -170,6 +170,9 @@ pub struct NearAction { pub method_names: Vec, /// `DeployContract`: size of the code blob. pub code_len: Option, + /// `DeployContract`: SHA-256 of the code, how NEAR identifies contract + /// code, so a signer can compare it with the artifact they meant to deploy. + pub code_hash: Option>, } impl From<&WireAction> for NearAction { @@ -182,6 +185,7 @@ impl From<&WireAction> for NearAction { WireAction::DeployContract { code } => NearAction { kind: NearActionKind::DeployContract, code_len: Some(code.len() as u32), + code_hash: Some(Sha256::digest(code).to_vec()), ..Default::default() }, WireAction::FunctionCall { @@ -609,6 +613,10 @@ mod tests { ] ); assert_eq!(tx.actions[1].code_len, Some(10)); + assert_eq!( + tx.actions[1].code_hash.as_deref(), + Some(Sha256::digest([0u8; 10]).as_slice()) + ); assert_eq!(tx.actions[2].amount, Some(5)); assert_eq!( (tx.actions[3].amount, tx.actions[3].public_key.as_deref()), diff --git a/quantus_sdk/rust/src/frb_generated.rs b/quantus_sdk/rust/src/frb_generated.rs index d7880a038..217391b61 100644 --- a/quantus_sdk/rust/src/frb_generated.rs +++ b/quantus_sdk/rust/src/frb_generated.rs @@ -1835,6 +1835,7 @@ impl SseDecode for crate::api::near::NearAction { let mut var_fullAccess = ::sse_decode(deserializer); let mut var_methodNames = >::sse_decode(deserializer); let mut var_codeLen = >::sse_decode(deserializer); + let mut var_codeHash = >>::sse_decode(deserializer); return crate::api::near::NearAction { kind: var_kind, target: var_target, @@ -1845,6 +1846,7 @@ impl SseDecode for crate::api::near::NearAction { full_access: var_fullAccess, method_names: var_methodNames, code_len: var_codeLen, + code_hash: var_codeHash, }; } } @@ -2340,6 +2342,7 @@ impl flutter_rust_bridge::IntoDart for crate::api::near::NearAction { self.full_access.into_into_dart().into_dart(), self.method_names.into_into_dart().into_dart(), self.code_len.into_into_dart().into_dart(), + self.code_hash.into_into_dart().into_dart(), ] .into_dart() } @@ -2683,6 +2686,7 @@ impl SseEncode for crate::api::near::NearAction { ::sse_encode(self.full_access, serializer); >::sse_encode(self.method_names, serializer); >::sse_encode(self.code_len, serializer); + >>::sse_encode(self.code_hash, serializer); } } From 0257dc8643ed44b7db5ee4301a1c9c4b6a98f4e7 Mon Sep 17 00:00:00 2001 From: Nikolaus Heger Date: Thu, 1 Oct 2026 16:13:50 +0800 Subject: [PATCH 09/16] cold-wallet: rename public key export button --- cold-wallet-app/lib/screens/show_key_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cold-wallet-app/lib/screens/show_key_screen.dart b/cold-wallet-app/lib/screens/show_key_screen.dart index 6dfa41b52..1e233eea4 100644 --- a/cold-wallet-app/lib/screens/show_key_screen.dart +++ b/cold-wallet-app/lib/screens/show_key_screen.dart @@ -82,7 +82,7 @@ class ShowKeyScreen extends ConsumerWidget { ), const SizedBox(height: 16), QuantusButton.simple( - label: 'Show NEAR public key', + label: 'Show public key', variant: ButtonVariant.staged, onTap: () { final keypair = keypairFor(ref, address); From e5f207425c9ab6f5d40cbd29fd1b57549c165b16 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 08:32:04 +0000 Subject: [PATCH 10/16] cold-wallet: re-indent NEAR call arguments without decoding their numbers A Dart num cannot hold every JSON number, so decode-then-encode showed 18446744073709551617 as 18446744073709552000.0. The JSON is now validated, then re-indented from its own text with every token copied through verbatim. --- .../lib/services/near_display.dart | 81 ++++++++++++++++++- cold-wallet-app/test/near_display_test.dart | 20 +++++ 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/cold-wallet-app/lib/services/near_display.dart b/cold-wallet-app/lib/services/near_display.dart index dbd8eeb89..96bd49b37 100644 --- a/cold-wallet-app/lib/services/near_display.dart +++ b/cold-wallet-app/lib/services/near_display.dart @@ -28,15 +28,90 @@ class NearDisplay { } /// Function-call arguments are JSON by convention; anything else is shown as hex. + /// + /// The JSON is re-indented from its own text, never decoded and re-encoded: + /// a Dart `num` cannot hold every JSON number, and an amount the signer + /// reads must be the amount the contract receives, digit for digit. static String argsText(Uint8List args) { if (args.isEmpty) return '(none)'; + final String text; try { - final decoded = json.decode(utf8.decode(args, allowMalformed: false)); - final pretty = const JsonEncoder.withIndent(' ').convert(decoded); - return isDisplaySafe(pretty, allowNewlines: true) ? pretty : '0x${hex.encode(args)}'; + text = utf8.decode(args, allowMalformed: false); + json.decode(text); } on FormatException { return '0x${hex.encode(args)}'; } + final pretty = reindentJson(text); + return isDisplaySafe(pretty, allowNewlines: true) ? pretty : '0x${hex.encode(args)}'; + } + + /// Re-indents [text], which must already be valid JSON, copying every token + /// through verbatim. Only whitespace outside strings is changed. + static String reindentJson(String text) { + final out = StringBuffer(); + var depth = 0; + var inString = false; + var escaped = false; + void newline() => out.write('\n${' ' * depth}'); + + for (var i = 0; i < text.length; i++) { + final c = text[i]; + if (inString) { + out.write(c); + if (escaped) { + escaped = false; + } else if (c == r'\') { + escaped = true; + } else if (c == '"') { + inString = false; + } + continue; + } + switch (c) { + case '"': + inString = true; + out.write(c); + case '{' || '[': + out.write(c); + if (_closesImmediately(text, i)) { + out.write(text[_nextNonSpace(text, i + 1)]); + i = _nextNonSpace(text, i + 1); + } else { + depth++; + newline(); + } + case '}' || ']': + depth--; + newline(); + out.write(c); + case ',': + out.write(c); + newline(); + case ':': + out.write(': '); + case ' ' || '\t' || '\n' || '\r': + break; + default: + out.write(c); + } + } + return out.toString(); + } + + static int _nextNonSpace(String text, int from) { + var i = from; + while (i < text.length && ' \t\n\r'.contains(text[i])) { + i++; + } + return i; + } + + /// Whether the bracket at [open] is followed by its closer, so `{}` and + /// `[]` stay on one line. + static bool _closesImmediately(String text, int open) { + final next = _nextNonSpace(text, open + 1); + if (next >= text.length) return false; + return (text[open] == '{' && text[next] == '}') || (text[open] == '[' && text[next] == ']'); } /// A string from the transaction — a method name, a key's method list — diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index cfd599cb5..ffd3707bf 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -61,6 +61,26 @@ void main() { expect(NearDisplay.argsText(Uint8List.fromList(utf8.encode('{"a":"x\\ny"}'))), '{\n "a": "x\\ny"\n}'); }); + test('argsText keeps every numeric token exactly as the contract will read it', () { + String shown(String json) => NearDisplay.argsText(Uint8List.fromList(utf8.encode(json))); + // Beyond 2^53 and beyond double precision: a decode/encode round trip + // would show 18446744073709552000.0 and 0.12345678901234568. + expect(shown('{"amount":18446744073709551617}'), '{\n "amount": 18446744073709551617\n}'); + expect(shown('{"limit":0.123456789012345678901234}'), '{\n "limit": 0.123456789012345678901234\n}'); + expect( + shown('{"e":1E+400,"n":-0,"s":"18446744073709551617"}'), + contains('"e": 1E+400,\n "n": -0,\n "s": "18446744073709551617"'), + ); + // Nesting, empty containers, and escapes inside strings survive untouched. + expect( + shown('{"a":[1,{"b":[]},{}],"c":"x{y}\\"z,:[]"}'), + '{\n "a": [\n 1,\n {\n "b": []\n },\n {}\n ],\n "c": "x{y}\\"z,:[]"\n}', + ); + expect(shown(' [ 1 , 2 ] '), '[\n 1,\n 2\n]'); + expect(shown('"just a string"'), '"just a string"'); + expect(shown('{"a":1'), startsWith('0x')); + }); + test('headline names a single action and counts several', () { expect(NearDisplay.headline(_tx(actions: [_action(NearActionKind.transfer)])), 'SEND'); expect( From cf1bbefe62c513c8be4a1bbee8705762255524d1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 08:32:04 +0000 Subject: [PATCH 11/16] cold-wallet: name the receiver in the NEAR account-control warning Key, code and account actions apply to receiver_id, which differs from the signer when a batch creates a sub-account. --- .../screens/sign_near_transaction_screen.dart | 60 ++----------------- .../sign_near_transaction_screen_test.dart | 19 ++++++ 2 files changed, 23 insertions(+), 56 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index 80b31f115..38bfffc35 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -6,6 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:quantus_cold_wallet/components/address_with_checkphrase.dart'; import 'package:quantus_cold_wallet/components/signature_qr_view.dart'; +import 'package:quantus_cold_wallet/components/signing_refusal_view.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; import 'package:quantus_cold_wallet/services/near_display.dart'; @@ -107,61 +108,8 @@ class _SignNearTransactionScreenState extends ConsumerState Navigator.popUntil(context, (r) => r.isFirst)), - ), - ); - } + Widget _errorView(BuildContext context, {required String title, required String message, required Widget detail}) => + SigningRefusalView(appBarTitle: 'Sign NEAR Transaction', title: title, message: message, detail: detail); Widget _reviewView(BuildContext context, String owner) { final colors = context.colorsV3; @@ -182,7 +130,7 @@ class _SignNearTransactionScreenState extends ConsumerState(find.text('ADD FULL ACCESS KEY')); expect(headline.style?.color, colors.semanticEmber); expect(find.text('This changes control of alice.testnet'), findsOneWidget); + expect(find.text('Nothing was signed.'), findsNothing); expect(find.text('1. ADD FULL ACCESS KEY'), findsOneWidget); expect(find.text('ed25519:4rN'), findsOneWidget); expect(find.text('Full access'), findsOneWidget); expect(find.text('Sign'), findsOneWidget); }); + testWidgets('a key added to a sub-account names that account, not the signer, in the warning', (tester) async { + await pump( + tester, + _tx( + receiver: 'vault.alice.testnet', + actions: [ + const NearAction(kind: NearActionKind.createAccount, fullAccess: false, methodNames: []), + const NearAction(kind: NearActionKind.addKey, publicKey: 'ed25519:4rN', fullAccess: true, methodNames: []), + ], + ), + ); + + expect(find.text('This changes control of vault.alice.testnet'), findsOneWidget); + expect(find.textContaining('control of alice.testnet'), findsNothing); + expect(find.text('1. CREATE ACCOUNT'), findsOneWidget); + expect(find.text('2. ADD FULL ACCESS KEY'), findsOneWidget); + }); + testWidgets('a contract deployment identifies the code by its hash, not just its size', (tester) async { final hash = Uint8List.fromList(List.generate(32, (i) => i)); await pump( From 305cfc520c89f33bcf1daa6bf882d619749fe8cc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 08:32:04 +0000 Subject: [PATCH 12/16] cold-wallet: share the QR panel and the refusal view across signing screens UrQrPanel renders any payload as an animated UR QR with the frame summary and pause/tuning control; its frames and paused flag live in providers (urQrFramesProvider, qrPausedProvider) rather than in widgets. SigningRefusalView is the one refusal layout for the Quantus and NEAR signing screens. The NEAR key export uses both, makes the full key selectable with a copy control, and says that the hot wallet does not yet read its QR. --- .../lib/components/signature_qr_view.dart | 74 ++---------- .../lib/components/signing_refusal_view.dart | 80 +++++++++++++ .../lib/components/ur_qr_panel.dart | 75 ++++++++++++ .../lib/providers/ur_qr_providers.dart | 39 +++++++ .../lib/screens/near_key_export_screen.dart | 103 ++++++++--------- .../lib/screens/sign_transaction_screen.dart | 60 +--------- cold-wallet-app/test/ur_qr_panel_test.dart | 107 ++++++++++++++++++ 7 files changed, 356 insertions(+), 182 deletions(-) create mode 100644 cold-wallet-app/lib/components/signing_refusal_view.dart create mode 100644 cold-wallet-app/lib/components/ur_qr_panel.dart create mode 100644 cold-wallet-app/lib/providers/ur_qr_providers.dart create mode 100644 cold-wallet-app/test/ur_qr_panel_test.dart diff --git a/cold-wallet-app/lib/components/signature_qr_view.dart b/cold-wallet-app/lib/components/signature_qr_view.dart index 44a105cec..81abe5e8e 100644 --- a/cold-wallet-app/lib/components/signature_qr_view.dart +++ b/cold-wallet-app/lib/components/signature_qr_view.dart @@ -1,49 +1,22 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; -import 'package:quantus_cold_wallet/providers/settings_providers.dart'; +import 'package:quantus_cold_wallet/components/ur_qr_panel.dart'; +import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; -/// Shows [signed] as an animated UR QR for the hot wallet to scan, with the -/// frame-size and speed controls the user tuned in settings. -class SignatureQrView extends ConsumerStatefulWidget { +/// The screen shown once a transaction is signed: [signed] as a UR QR for +/// the hot wallet to scan, and a Done button back to home. +class SignatureQrView extends StatelessWidget { final Uint8List signed; final String instruction; const SignatureQrView({super.key, required this.signed, required this.instruction}); - @override - ConsumerState createState() => _SignatureQrViewState(); -} - -class _SignatureQrViewState extends ConsumerState { - List? _urParts; - int? _urPartsBytes; - bool _qrPaused = false; - - /// Pauses the animation and opens the tuning sheet; resumes when it closes. - Future _pauseAndTune() async { - setState(() => _qrPaused = true); - await BottomSheetContainer.show( - context, - builder: (ctx) => const BottomSheetContainer(title: 'QR display options', child: QrTuningControls()), - ); - if (mounted) setState(() => _qrPaused = false); - } - @override Widget build(BuildContext context) { final colors = context.colorsV3; final text = context.themeTextV3; - final settings = ref.watch(coldSettingsProvider); - - if (_urParts == null || _urPartsBytes != settings.qrBytes) { - _urParts = encodeUrForQr(data: widget.signed, maxFragmentLength: settings.qrBytes); - _urPartsBytes = settings.qrBytes; - } - final parts = _urParts!; return ScaffoldBase( appBar: const V2AppBar(title: 'Signature', showBackButton: false), @@ -53,45 +26,12 @@ class _SignatureQrViewState extends ConsumerState { children: [ const SizedBox(height: 8), Text( - widget.instruction, + instruction, style: text.body.copyWith(color: colors.textMuted), textAlign: TextAlign.center, ), const SizedBox(height: 24), - Center( - child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: _qrPaused), - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' - '${settings.qrBytes} bytes', - style: text.caption.copyWith(color: colors.textMuted), - ), - if (parts.length > 1) ...[ - const SizedBox(width: 12), - GestureDetector( - onTap: _pauseAndTune, - child: Container( - width: 32, - height: 32, - decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), - child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), - ), - ), - ], - ], - ), - if (parts.length > 1) ...[ - const SizedBox(height: 16), - Text( - 'Animated QR — keep both devices steady until the hot wallet finishes scanning.', - style: text.caption.copyWith(color: colors.textMuted), - textAlign: TextAlign.center, - ), - ], + UrQrPanel(payload: UrPayload(signed)), ], ), ), diff --git a/cold-wallet-app/lib/components/signing_refusal_view.dart b/cold-wallet-app/lib/components/signing_refusal_view.dart new file mode 100644 index 000000000..ee5302950 --- /dev/null +++ b/cold-wallet-app/lib/components/signing_refusal_view.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +/// Why a signing request was refused, with the one detail that identifies it +/// (the account, the key, the decoder's message) and a way back to home. +/// Shared by every signing screen so a refusal looks the same whatever the +/// chain. +class SigningRefusalView extends StatelessWidget { + final String appBarTitle; + final String title; + final String message; + final Widget detail; + + const SigningRefusalView({ + super.key, + required this.appBarTitle, + required this.title, + required this.message, + required this.detail, + }); + + @override + Widget build(BuildContext context) { + final colors = context.colorsV3; + final text = context.themeTextV3; + return ScaffoldBase( + appBar: V2AppBar(title: appBarTitle), + // The detail can be as long as a decoder's message, which no layout can + // bound, so this column scrolls rather than overflowing on a small screen. + mainContent: Center( + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Center( + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration(color: colors.semanticEmber.useOpacity(0.12), shape: BoxShape.circle), + child: Icon(Icons.error_outline, size: 72, color: colors.semanticEmber), + ), + ), + const SizedBox(height: 24), + Text( + title, + style: text.titleHero.copyWith(color: colors.semanticEmber), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + Text( + message, + style: text.bodyLarge.copyWith(color: colors.textContent), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colors.semanticEmber.useOpacity(0.08), + borderRadius: context.radiusV3.mdBorder, + border: Border.all(color: colors.semanticEmber), + ), + child: detail, + ), + const SizedBox(height: 20), + Text( + 'Nothing was signed.', + style: text.bodyEmphasis.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + bottomContent: ScaffoldBaseBottomContent( + child: QuantusButton.simple(label: 'Back to home', onTap: () => Navigator.popUntil(context, (r) => r.isFirst)), + ), + ); + } +} diff --git a/cold-wallet-app/lib/components/ur_qr_panel.dart b/cold-wallet-app/lib/components/ur_qr_panel.dart new file mode 100644 index 000000000..ef26dff00 --- /dev/null +++ b/cold-wallet-app/lib/components/ur_qr_panel.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; +import 'package:quantus_cold_wallet/providers/settings_providers.dart'; +import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; + +/// An animated UR QR of [payload] with its frame/FPS/bytes summary and, for +/// multi-frame payloads, a pause button that opens the tuning sheet. The +/// frames and the paused flag live in providers, so every screen that shows +/// a QR behaves the same and keeps no QR state of its own. +class UrQrPanel extends ConsumerWidget { + final UrPayload payload; + + const UrQrPanel({super.key, required this.payload}); + + Future _pauseAndTune(BuildContext context, WidgetRef ref) async { + final paused = ref.read(qrPausedProvider.notifier); + paused.pause(); + await BottomSheetContainer.show( + context, + builder: (ctx) => const BottomSheetContainer(title: 'QR display options', child: QrTuningControls()), + ); + paused.resume(); + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + final colors = context.colorsV3; + final text = context.themeTextV3; + final settings = ref.watch(coldSettingsProvider); + final parts = ref.watch(urQrFramesProvider(payload)); + final paused = ref.watch(qrPausedProvider); + + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Center( + child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: paused), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' + '${settings.qrBytes} bytes', + style: text.caption.copyWith(color: colors.textMuted), + ), + if (parts.length > 1) ...[ + const SizedBox(width: 12), + GestureDetector( + onTap: () => _pauseAndTune(context, ref), + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), + child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), + ), + ), + ], + ], + ), + if (parts.length > 1) ...[ + const SizedBox(height: 16), + Text( + 'Animated QR — keep both devices steady until the other device finishes scanning.', + style: text.caption.copyWith(color: colors.textMuted), + textAlign: TextAlign.center, + ), + ], + ], + ); + } +} diff --git a/cold-wallet-app/lib/providers/ur_qr_providers.dart b/cold-wallet-app/lib/providers/ur_qr_providers.dart new file mode 100644 index 000000000..77ef692ef --- /dev/null +++ b/cold-wallet-app/lib/providers/ur_qr_providers.dart @@ -0,0 +1,39 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:quantus_cold_wallet/providers/settings_providers.dart'; + +/// Bytes to show as a UR QR, equal by content so they can key a provider +/// family: two screens showing the same bytes share one set of frames. +@immutable +class UrPayload { + final Uint8List bytes; + + const UrPayload(this.bytes); + + @override + bool operator ==(Object other) => other is UrPayload && listEquals(bytes, other.bytes); + + @override + int get hashCode => Object.hashAll(bytes); +} + +/// The UR frames for a payload at the user's bytes-per-frame setting. +/// Recomputed only when that setting changes; dropped when nothing shows it. +final urQrFramesProvider = Provider.autoDispose.family, UrPayload>((ref, payload) { + final qrBytes = ref.watch(coldSettingsProvider.select((s) => s.qrBytes)); + return encodeUrForQr(data: payload.bytes, maxFragmentLength: qrBytes); +}); + +/// Whether the QR animation on screen is paused, as while its tuning sheet is +/// open. One QR is shown at a time, so one flag serves every QR screen. +class QrPausedController extends Notifier { + @override + bool build() => false; + + void pause() => state = true; + + void resume() => state = false; +} + +final qrPausedProvider = NotifierProvider.autoDispose(QrPausedController.new); diff --git a/cold-wallet-app/lib/screens/near_key_export_screen.dart b/cold-wallet-app/lib/screens/near_key_export_screen.dart index 4df735090..c7716def6 100644 --- a/cold-wallet-app/lib/screens/near_key_export_screen.dart +++ b/cold-wallet-app/lib/screens/near_key_export_screen.dart @@ -1,50 +1,40 @@ import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter/services.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:quantus_cold_wallet/components/qr_tuning_controls.dart'; -import 'package:quantus_cold_wallet/providers/settings_providers.dart'; +import 'package:quantus_cold_wallet/components/ur_qr_panel.dart'; +import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; /// Shows one account's ML-DSA-65 key in NEAR's form, as an animated UR QR -/// for a hot wallet or CLI to scan and as text for anyone copying it by hand. +/// and as selectable text with a copy control. /// /// The key is 1952 bytes, well past a single QR, hence the animation. The /// `ml-dsa-65-hash:` handle under it is what NEAR lists on chain once the key /// is added, so a user can confirm there that the key they added is this one. -class NearKeyExportScreen extends ConsumerStatefulWidget { +/// Nothing here touches NEAR: adding the key to an account is done elsewhere, +/// with `near account add-key` or a wallet that reads this export. +class NearKeyExportScreen extends StatelessWidget { final NearPublicKeyExport export; final String handle; const NearKeyExportScreen({super.key, required this.export, required this.handle}); - @override - ConsumerState createState() => _NearKeyExportScreenState(); -} - -class _NearKeyExportScreenState extends ConsumerState { - List? _urParts; - int? _urPartsBytes; - bool _qrPaused = false; - - Future _pauseAndTune() async { - setState(() => _qrPaused = true); - await BottomSheetContainer.show( - context, - builder: (ctx) => const BottomSheetContainer(title: 'QR display options', child: QrTuningControls()), + Future _copyKey(BuildContext context) async { + await Clipboard.setData(ClipboardData(text: export.nearPublicKey)); + if (!context.mounted) return; + final colors = context.colorsV3; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('NEAR public key copied', style: context.themeTextV3.body.copyWith(color: colors.textContent)), + backgroundColor: colors.bgSurface2, + duration: const Duration(seconds: 2), + ), ); - if (mounted) setState(() => _qrPaused = false); } @override Widget build(BuildContext context) { final colors = context.colorsV3; final text = context.themeTextV3; - final settings = ref.watch(coldSettingsProvider); - - if (_urParts == null || _urPartsBytes != settings.qrBytes) { - _urParts = encodeUrForQr(data: widget.export.encode(), maxFragmentLength: settings.qrBytes); - _urPartsBytes = settings.qrBytes; - } - final parts = _urParts!; return ScaffoldBase( appBar: const V2AppBar(title: 'NEAR Public Key'), @@ -54,47 +44,44 @@ class _NearKeyExportScreenState extends ConsumerState { children: [ const SizedBox(height: 8), Text( - 'Scan with the device that prepares your NEAR transactions. Add this key to your NEAR account as a ' - 'full access key, then transactions for that account can be signed here.', + 'This is the account\'s key in NEAR\'s form. Add it to a NEAR account as a full access key — for now ' + 'by copying the key into the NEAR CLI, as the Quantus hot wallet does not yet read this QR — and ' + 'transactions for that account can then be reviewed and signed here.', style: text.body.copyWith(color: colors.textMuted), textAlign: TextAlign.center, ), const SizedBox(height: 24), - Center( - child: AnimatedUrQr(parts: parts, fps: settings.qrFps, paused: _qrPaused), - ), - const SizedBox(height: 12), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - '${parts.length} ${parts.length == 1 ? 'frame' : 'frames'} · ${settings.qrFps} FPS · ' - '${settings.qrBytes} bytes', - style: text.caption.copyWith(color: colors.textMuted), - ), - if (parts.length > 1) ...[ - const SizedBox(width: 12), - GestureDetector( - onTap: _pauseAndTune, - child: Container( - width: 32, - height: 32, - decoration: BoxDecoration(color: colors.bgSurface2, shape: BoxShape.circle), - child: Icon(Icons.pause_rounded, size: 20, color: colors.textContent), - ), - ), - ], - ], - ), + UrQrPanel(payload: UrPayload(export.encode())), const SizedBox(height: 24), DetailSummaryRow.stacked( label: 'On-chain handle', - value: widget.handle, + value: handle, monospace: true, note: 'How NEAR lists this key once it is added. Check it matches on the explorer.', ), - DetailSummaryRow.stacked(label: 'Quantus account', value: widget.export.address, monospace: true), - DetailSummaryRow.stacked(label: 'Full key', value: widget.export.nearPublicKey, monospace: true), + DetailSummaryRow.stacked(label: 'Quantus account', value: export.address, monospace: true), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text('FULL KEY', style: text.labelData.copyWith(color: colors.textMuted)), + ), + QuantusIconButton.ghost( + icon: Icons.copy_rounded, + size: IconButtonSize.small, + onTap: () => _copyKey(context), + ), + ], + ), + const SizedBox(height: 4), + SelectableText(export.nearPublicKey, style: text.dataAddress.copyWith(color: colors.textContent)), + ], + ), + ), const SizedBox(height: 24), ], ), diff --git a/cold-wallet-app/lib/screens/sign_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_transaction_screen.dart index afc84bda4..5c3677873 100644 --- a/cold-wallet-app/lib/screens/sign_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_transaction_screen.dart @@ -8,6 +8,7 @@ import 'package:quantus_cold_wallet/app_version.dart'; import 'package:quantus_cold_wallet/components/address_with_checkphrase.dart'; import 'package:quantus_cold_wallet/components/call_detail_view.dart'; import 'package:quantus_cold_wallet/components/signature_qr_view.dart'; +import 'package:quantus_cold_wallet/components/signing_refusal_view.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; /// Reviews a scanned signing payload and, on approval, produces the signature QR. @@ -128,63 +129,8 @@ class _SignTransactionScreenState extends ConsumerState { }; } - Widget _errorView(BuildContext context, {required String title, required String message, required Widget detail}) { - final colors = context.colorsV3; - final text = context.themeTextV3; - return ScaffoldBase( - appBar: const V2AppBar(title: 'Sign Transaction'), - // The detail can be as long as the decoder's message, which no layout can - // bound, so this column scrolls rather than overflowing on a small screen. - mainContent: Center( - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration(color: colors.semanticEmber.useOpacity(0.12), shape: BoxShape.circle), - child: Icon(Icons.error_outline, size: 72, color: colors.semanticEmber), - ), - ), - const SizedBox(height: 24), - Text( - title, - style: text.titleHero.copyWith(color: colors.semanticEmber), - textAlign: TextAlign.center, - ), - const SizedBox(height: 16), - Text( - message, - style: text.bodyLarge.copyWith(color: colors.textContent), - textAlign: TextAlign.center, - ), - const SizedBox(height: 20), - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: colors.semanticEmber.useOpacity(0.08), - borderRadius: context.radiusV3.mdBorder, - border: Border.all(color: colors.semanticEmber), - ), - child: detail, - ), - const SizedBox(height: 20), - Text( - 'Nothing was signed.', - style: text.bodyEmphasis.copyWith(color: colors.textMuted), - textAlign: TextAlign.center, - ), - ], - ), - ), - ), - bottomContent: ScaffoldBaseBottomContent( - child: QuantusButton.simple(label: 'Back to home', onTap: () => Navigator.popUntil(context, (r) => r.isFirst)), - ), - ); - } + Widget _errorView(BuildContext context, {required String title, required String message, required Widget detail}) => + SigningRefusalView(appBarTitle: 'Sign Transaction', title: title, message: message, detail: detail); Widget _reviewView(BuildContext context, ParsedPayload parsed) { final colors = context.colorsV3; diff --git a/cold-wallet-app/test/ur_qr_panel_test.dart b/cold-wallet-app/test/ur_qr_panel_test.dart new file mode 100644 index 000000000..4b7e0c8ee --- /dev/null +++ b/cold-wallet-app/test/ur_qr_panel_test.dart @@ -0,0 +1,107 @@ +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_cold_wallet/components/signing_refusal_view.dart'; +import 'package:quantus_cold_wallet/components/ur_qr_panel.dart'; +import 'package:quantus_cold_wallet/providers/settings_providers.dart'; +import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const colors = AppColorsV3.dark(); + + group('UrPayload', () { + test('is equal by content, so two screens share one frame set', () { + final a = UrPayload(Uint8List.fromList([1, 2, 3])); + final b = UrPayload(Uint8List.fromList([1, 2, 3])); + final c = UrPayload(Uint8List.fromList([1, 2, 4])); + expect(a, b); + expect(a.hashCode, b.hashCode); + expect(a, isNot(c)); + }); + }); + + group('qrPausedProvider', () { + test('starts running and toggles', () { + final container = ProviderContainer(); + addTearDown(container.dispose); + expect(container.read(qrPausedProvider), isFalse); + container.read(qrPausedProvider.notifier).pause(); + expect(container.read(qrPausedProvider), isTrue); + container.read(qrPausedProvider.notifier).resume(); + expect(container.read(qrPausedProvider), isFalse); + }); + }); + + testWidgets('UrQrPanel shows the frame summary and a pause control for animated payloads', (tester) async { + final frames = List.generate(7, (i) => 'ur:bytes/${i + 1}-7/abcd'); + await tester.pumpWidget( + ProviderScope( + overrides: [urQrFramesProvider.overrideWith((ref, payload) => frames)], + child: Builder( + builder: (context) => MaterialApp( + theme: AppTheme.darkTheme(context), + home: Scaffold(body: UrQrPanel(payload: UrPayload(Uint8List(5000)))), + ), + ), + ), + ); + await tester.pump(); + + expect(find.byType(AnimatedUrQr), findsOneWidget); + expect( + find.text('7 frames · ${ColdSettings.defaultQrFps} FPS · ${ColdSettings.defaultQrBytes} bytes'), + findsOneWidget, + ); + expect(find.byIcon(Icons.pause_rounded), findsOneWidget); + expect(find.textContaining('keep both devices steady'), findsOneWidget); + }); + + testWidgets('UrQrPanel hides the pause control for a single frame', (tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + urQrFramesProvider.overrideWith((ref, payload) => const ['ur:bytes/abcd']), + ], + child: Builder( + builder: (context) => MaterialApp( + theme: AppTheme.darkTheme(context), + home: Scaffold(body: UrQrPanel(payload: UrPayload(Uint8List(5)))), + ), + ), + ), + ); + await tester.pump(); + + expect(find.textContaining('1 frame ·'), findsOneWidget); + expect(find.byIcon(Icons.pause_rounded), findsNothing); + }); + + testWidgets('SigningRefusalView shows the reason, the detail and that nothing was signed', (tester) async { + await tester.pumpWidget( + Builder( + builder: (context) => MaterialApp( + theme: AppTheme.darkTheme(context), + home: const SigningRefusalView( + appBarTitle: 'Sign Something', + title: 'Refused', + message: 'Because.', + detail: Text('the detail'), + ), + ), + ), + ); + await tester.pump(); + + expect(find.text('Sign Something'), findsOneWidget); + expect(tester.widget(find.text('Refused')).style?.color, colors.semanticEmber); + expect(find.text('Because.'), findsOneWidget); + expect(find.text('the detail'), findsOneWidget); + expect(find.text('Nothing was signed.'), findsOneWidget); + expect(find.text('Back to home'), findsOneWidget); + }); +} From 71bc5fa56153b55092b54c91cf8212dc212f39ef Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 10:54:38 +0000 Subject: [PATCH 13/16] cold-wallet: make the NEAR network check a suffix hint, not a validity claim NEAR account IDs do not encode a network. Only sub-accounts ending in the other network's customary top-level name (.testnet on mainnet, .near on testnet) are mentioned; top-level accounts, implicit (64 hex, 0x+40 hex) and deterministic (0s+40 hex) IDs, and other hierarchies are never flagged. The copy names the label as the requesting wallet's and says names do not prove a network. --- .../screens/sign_near_transaction_screen.dart | 2 +- .../lib/services/near_display.dart | 34 +++++++++++-------- cold-wallet-app/test/near_display_test.dart | 29 +++++++++++++--- .../sign_near_transaction_screen_test.dart | 10 +++--- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index 38bfffc35..0fc4fbd33 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -126,7 +126,7 @@ class _SignNearTransactionScreenState extends ConsumerState '.near', - 'testnet' => '.testnet', + final otherTld = switch (network) { + 'mainnet' => '.testnet', + 'testnet' => '.near', _ => throw ArgumentError.value(network, 'network', 'not a supported NEAR network'), }; - final mismatched = [tx.signerId, tx.receiverId].where((id) => _isNamed(id) && !id.endsWith(tld)).toList(); - if (mismatched.isEmpty) return null; - return 'This request says $network, but ${mismatched.join(' and ')} ' - '${mismatched.length == 1 ? 'is not a' : 'are not'} $network account name' - '${mismatched.length == 1 ? '' : 's'}. Check which network your hot wallet is on before signing.'; + final suspicious = [tx.signerId, tx.receiverId].where((id) => id.endsWith(otherTld)).toList(); + if (suspicious.isEmpty) return null; + final plural = suspicious.length > 1; + return 'The requesting wallet labelled this $network, but ${suspicious.join(' and ')} ' + '${plural ? 'end' : 'ends'} in $otherTld, the name usually used on ${_networkOf(otherTld)}. ' + 'Account names do not prove a network; check which one your hot wallet is on before signing.'; } - /// Implicit accounts (64 hex, or `0x` + 40 hex) belong to every network. - static bool _isNamed(String accountId) => !RegExp(r'^([0-9a-f]{64}|0x[0-9a-f]{40})$').hasMatch(accountId); + static String _networkOf(String tld) => tld == '.near' ? 'mainnet' : 'testnet'; } diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index ffd3707bf..888b99cff 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -110,19 +110,40 @@ void main() { }); group('NearDisplay.networkMismatch', () { - test('is silent when names match the network or are implicit', () { + test('is silent when names use the labelled network\'s suffix', () { expect(NearDisplay.networkMismatch(_tx(), 'testnet'), isNull); expect(NearDisplay.networkMismatch(_tx(signer: 'alice.near', receiver: 'bob.near'), 'mainnet'), isNull); + }); + + test('never flags accounts whose names carry no network convention', () { + // Top-level accounts exist on every network and are created by the registrar. + expect(NearDisplay.networkMismatch(_tx(signer: 'near', receiver: 'alice.near'), 'mainnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(signer: 'registrar', receiver: 'com'), 'testnet'), isNull); + // NEAR-implicit, ETH-implicit and NEAR-deterministic account IDs. expect(NearDisplay.networkMismatch(_tx(signer: 'a' * 64, receiver: '0x${'b' * 40}'), 'mainnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(signer: '0s${'c' * 40}', receiver: 'a' * 64), 'testnet'), isNull); + // Other sub-account hierarchies (e.g. .com, .aurora, .tg) say nothing about the network. + expect(NearDisplay.networkMismatch(_tx(signer: 'alice.aurora', receiver: 'bob.tg'), 'mainnet'), isNull); + expect(NearDisplay.networkMismatch(_tx(signer: 'alice.aurora', receiver: 'bob.tg'), 'testnet'), isNull); + }); + + test('rejects a label the request envelope should never have let through', () { expect(() => NearDisplay.networkMismatch(_tx(), 'localnet'), throwsArgumentError); expect(() => NearDisplay.networkMismatch(_tx(), 'testnet '), throwsArgumentError); }); - test('names each account that does not belong to the network', () { - expect(NearDisplay.networkMismatch(_tx(), 'mainnet'), contains('alice.testnet and bob.testnet are not mainnet')); + test('names each account that uses the other network\'s suffix, as a hint', () { + final both = NearDisplay.networkMismatch(_tx(), 'mainnet')!; + expect(both, contains('The requesting wallet labelled this mainnet')); + expect(both, contains('alice.testnet and bob.testnet end in .testnet, the name usually used on testnet')); + expect(both, contains('Account names do not prove a network')); expect( NearDisplay.networkMismatch(_tx(signer: 'alice.near'), 'mainnet'), - contains('bob.testnet is not a mainnet account name'), + contains('bob.testnet ends in .testnet'), + ); + expect( + NearDisplay.networkMismatch(_tx(signer: 'alice.near', receiver: 'bob.testnet'), 'testnet'), + contains('alice.near ends in .near, the name usually used on mainnet'), ); }); }); diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart index df13b6ff3..ba535cd9b 100644 --- a/cold-wallet-app/test/sign_near_transaction_screen_test.dart +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -91,7 +91,7 @@ void main() { expect(find.text(owner), findsOneWidget); expect(find.text('amber glacier quartz'), findsOneWidget); expect(find.text('Sign'), findsOneWidget); - expect(find.textContaining('Network does not match'), findsNothing); + expect(find.textContaining('Check the network'), findsNothing); expect(find.textContaining('changes control'), findsNothing); }); @@ -187,12 +187,12 @@ void main() { expect(find.text('ref-finance.testnet'), findsOneWidget); }); - testWidgets('warns when the account names do not belong to the labelled network', (tester) async { + testWidgets('hints when the account names use the other network\'s suffix', (tester) async { await pump(tester, _tx(actions: [_transfer(_near)]), network: 'mainnet'); expect(find.text('NEAR · MAINNET'), findsOneWidget); - expect(find.text('Network does not match'), findsOneWidget); - expect(find.textContaining('alice.testnet and bob.testnet are not mainnet'), findsOneWidget); + expect(find.text('Check the network'), findsOneWidget); + expect(find.textContaining('alice.testnet and bob.testnet end in .testnet'), findsOneWidget); expect(find.text('Sign'), findsOneWidget); }); @@ -204,7 +204,7 @@ void main() { ); expect(find.text('NEAR · MAINNET'), findsOneWidget); - expect(find.text('Network does not match'), findsNothing); + expect(find.text('Check the network'), findsNothing); }); testWidgets('refuses a key no account here holds, and offers no Sign', (tester) async { From 935c5259817faa937c41451cf74f27adf74f2c87 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 11:07:04 +0000 Subject: [PATCH 14/16] cold-wallet: warn about NEAR account creation even without a key action CreateAccount brings a new account into existence under the signer's authority; it now counts as dangerous like the other account changes. The banner names the receiver as created when nothing else in the batch is dangerous, and as a control change otherwise. --- .../screens/sign_near_transaction_screen.dart | 9 +++--- .../lib/services/near_display.dart | 21 ++++++++++--- cold-wallet-app/test/near_display_test.dart | 31 +++++++++++++++++-- .../sign_near_transaction_screen_test.dart | 24 ++++++++++++++ 4 files changed, 75 insertions(+), 10 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index 0fc4fbd33..35288851c 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -114,7 +114,8 @@ class _SignNearTransactionScreenState extends ConsumerState 'DELETE ACCOUNT', }; - /// Actions that change who controls the account or what code runs under it, - /// rather than moving a stated amount. Shown in warning colour. + /// Actions that change which accounts exist, who controls them or what code + /// runs under them, rather than moving a stated amount. Shown in warning + /// colour. static bool isDangerous(NearAction action) => switch (action.kind) { - NearActionKind.addKey || NearActionKind.deleteKey || NearActionKind.deleteAccount => true, + NearActionKind.createAccount || NearActionKind.deleteAccount => true, + NearActionKind.addKey || NearActionKind.deleteKey => true, NearActionKind.deployContract || NearActionKind.stake => true, - NearActionKind.transfer || NearActionKind.functionCall || NearActionKind.createAccount => false, + NearActionKind.transfer || NearActionKind.functionCall => false, }; + /// Title of the account-change warning, or null when nothing in [tx] is + /// [isDangerous]. Creating the receiver is called out by name, since the + /// signer is bringing a new account into existence; anything else that + /// is dangerous changes control of an account that already exists. + static String? accountChangeTitle(NearTransaction tx) { + final dangerous = tx.actions.where(isDangerous).toList(); + if (dangerous.isEmpty) return null; + if (dangerous.every((a) => a.kind == NearActionKind.createAccount)) return 'This creates ${tx.receiverId}'; + return 'This changes control of ${tx.receiverId}'; + } + /// What the whole transaction does, in one line: the single action's title, /// or the count when there are several. static String headline(NearTransaction tx) { diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index 888b99cff..49d74daa8 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -94,8 +94,9 @@ void main() { ); }); - test('key, code and account changes are dangerous; sends and calls are not', () { + test('account, key and code changes are dangerous; sends and calls are not', () { for (final kind in [ + NearActionKind.createAccount, NearActionKind.addKey, NearActionKind.deleteKey, NearActionKind.deleteAccount, @@ -104,11 +105,37 @@ void main() { ]) { expect(NearDisplay.isDangerous(_action(kind)), isTrue, reason: '$kind'); } - for (final kind in [NearActionKind.transfer, NearActionKind.functionCall, NearActionKind.createAccount]) { + for (final kind in [NearActionKind.transfer, NearActionKind.functionCall]) { expect(NearDisplay.isDangerous(_action(kind)), isFalse, reason: '$kind'); } }); + test('the account-change title names creation apart from control changes', () { + expect(NearDisplay.accountChangeTitle(_tx()), isNull); + expect( + NearDisplay.accountChangeTitle( + _tx( + receiver: 'vault.alice.testnet', + actions: [_action(NearActionKind.createAccount), _action(NearActionKind.transfer)], + ), + ), + 'This creates vault.alice.testnet', + ); + expect( + NearDisplay.accountChangeTitle( + _tx( + receiver: 'vault.alice.testnet', + actions: [_action(NearActionKind.createAccount), _action(NearActionKind.addKey)], + ), + ), + 'This changes control of vault.alice.testnet', + ); + expect( + NearDisplay.accountChangeTitle(_tx(actions: [_action(NearActionKind.deleteKey)])), + 'This changes control of bob.testnet', + ); + }); + group('NearDisplay.networkMismatch', () { test('is silent when names use the labelled network\'s suffix', () { expect(NearDisplay.networkMismatch(_tx(), 'testnet'), isNull); diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart index ba535cd9b..d81f9a1b7 100644 --- a/cold-wallet-app/test/sign_near_transaction_screen_test.dart +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -134,6 +134,30 @@ void main() { expect(find.text('2. ADD FULL ACCESS KEY'), findsOneWidget); }); + testWidgets('creating an account with no key action is still warned about, by name', (tester) async { + await pump( + tester, + _tx( + receiver: 'vault.alice.testnet', + actions: [ + const NearAction(kind: NearActionKind.createAccount, fullAccess: false, methodNames: []), + _transfer(_near), + ], + ), + ); + + expect(find.text('This creates vault.alice.testnet'), findsOneWidget); + expect(find.textContaining('changes control'), findsNothing); + final headline = tester.widget(find.text('2 ACTIONS')); + expect(headline.style?.color, colors.semanticEmber); + final row = tester.widget(find.text('1. CREATE ACCOUNT')); + expect(row.style?.color, colors.semanticEmber); + final sendRow = tester.widget(find.text('2. SEND')); + expect(sendRow.style?.color, colors.semanticLilac); + expect(find.text('1 NEAR'), findsOneWidget); + expect(find.text('Sign'), findsOneWidget); + }); + testWidgets('a contract deployment identifies the code by its hash, not just its size', (tester) async { final hash = Uint8List.fromList(List.generate(32, (i) => i)); await pump( From 83904289af0d3ad4ffc7c966ed6db5fc2f673dd4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 12:08:34 +0000 Subject: [PATCH 15/16] cold-wallet: mark the NEAR network label as the requesting wallet's unverified claim The label is not part of the signed bytes and nothing in them names a network, so every review now shows UNVERIFIED beside it with a standing notice that the signature is valid wherever the signer account and nonce exist, and tells the signer which network to confirm on the hot wallet. --- .../screens/sign_near_transaction_screen.dart | 35 ++++++++++++++----- .../lib/services/near_display.dart | 9 +++++ cold-wallet-app/test/near_display_test.dart | 7 ++++ .../sign_near_transaction_screen_test.dart | 21 +++++++++++ 4 files changed, 63 insertions(+), 9 deletions(-) diff --git a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart index 35288851c..37b02adfc 100644 --- a/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart +++ b/cold-wallet-app/lib/screens/sign_near_transaction_screen.dart @@ -189,18 +189,35 @@ class _SignNearTransactionScreenState extends ConsumerState + 'Network as stated by the requesting wallet. The transaction bytes do not name a network, so this ' + 'cannot be checked here: the signature will be valid wherever the signer account and nonce exist. ' + 'Confirm your hot wallet is on $network before signing.'; + /// A naming-convention hint, not a verdict on the request. NEAR account IDs /// do not encode a network: the `network` label comes from the requesting /// wallet, and this only notices when a sub-account's top-level name is the diff --git a/cold-wallet-app/test/near_display_test.dart b/cold-wallet-app/test/near_display_test.dart index 49d74daa8..a847c03df 100644 --- a/cold-wallet-app/test/near_display_test.dart +++ b/cold-wallet-app/test/near_display_test.dart @@ -136,6 +136,13 @@ void main() { ); }); + test('the network disclaimer attributes the label and names the network to confirm', () { + final text = NearDisplay.networkDisclaimer('mainnet'); + expect(text, startsWith('Network as stated by the requesting wallet.')); + expect(text, contains('do not name a network')); + expect(text, endsWith('Confirm your hot wallet is on mainnet before signing.')); + }); + group('NearDisplay.networkMismatch', () { test('is silent when names use the labelled network\'s suffix', () { expect(NearDisplay.networkMismatch(_tx(), 'testnet'), isNull); diff --git a/cold-wallet-app/test/sign_near_transaction_screen_test.dart b/cold-wallet-app/test/sign_near_transaction_screen_test.dart index d81f9a1b7..b175e9aa9 100644 --- a/cold-wallet-app/test/sign_near_transaction_screen_test.dart +++ b/cold-wallet-app/test/sign_near_transaction_screen_test.dart @@ -80,6 +80,9 @@ void main() { await pump(tester, _tx(actions: [_transfer(_near * BigInt.from(15) ~/ BigInt.from(10))])); expect(find.text('NEAR · TESTNET'), findsOneWidget); + expect(find.text('UNVERIFIED'), findsOneWidget); + expect(find.textContaining('Network as stated by the requesting wallet'), findsOneWidget); + expect(find.textContaining('Confirm your hot wallet is on testnet'), findsOneWidget); final headline = tester.widget(find.text('SEND')); expect(headline.style?.color, colors.accentFlare); expect(find.textContaining('1.5', findRichText: true), findsOneWidget); @@ -220,6 +223,23 @@ void main() { expect(find.text('Sign'), findsOneWidget); }); + testWidgets('network-neutral account IDs still see the network marked as the wallet\'s claim', (tester) async { + // An implicit signer and an ETH-implicit receiver exist on every network: + // the suffix hint has nothing to say, so the standing notice is all there is. + await pump( + tester, + _tx(signer: 'a' * 64, receiver: '0x${'b' * 40}', actions: [_transfer(_near)]), + network: 'testnet', + ); + + expect(find.text('NEAR · TESTNET'), findsOneWidget); + expect(find.text('UNVERIFIED'), findsOneWidget); + expect(find.textContaining('The transaction bytes do not name a network'), findsOneWidget); + expect(find.textContaining('Confirm your hot wallet is on testnet'), findsOneWidget); + expect(find.text('Check the network'), findsNothing); + expect(find.text('Sign'), findsOneWidget); + }); + testWidgets('a mainnet transfer labelled mainnet is reviewed without a mismatch warning', (tester) async { await pump( tester, @@ -228,6 +248,7 @@ void main() { ); expect(find.text('NEAR · MAINNET'), findsOneWidget); + expect(find.text('UNVERIFIED'), findsOneWidget); expect(find.text('Check the network'), findsNothing); }); From 02f59a6845d69a7edccd7f488f3dc768c9a2a27d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 1 Oct 2026 12:14:19 +0000 Subject: [PATCH 16/16] cold-wallet: present the exported key as the account's public key, not a NEAR key An ML-DSA-65 key is a Quantus key; NEAR is one system that registers it directly. The section, screen title, copy and copy-toast now say public key, with NEAR named only where its text form and on-chain handle apply. --- ...een.dart => public_key_export_screen.dart} | 34 ++++++------- .../lib/screens/show_key_screen.dart | 20 ++++---- .../test/public_key_export_screen_test.dart | 48 +++++++++++++++++++ 3 files changed, 77 insertions(+), 25 deletions(-) rename cold-wallet-app/lib/screens/{near_key_export_screen.dart => public_key_export_screen.dart} (64%) create mode 100644 cold-wallet-app/test/public_key_export_screen_test.dart diff --git a/cold-wallet-app/lib/screens/near_key_export_screen.dart b/cold-wallet-app/lib/screens/public_key_export_screen.dart similarity index 64% rename from cold-wallet-app/lib/screens/near_key_export_screen.dart rename to cold-wallet-app/lib/screens/public_key_export_screen.dart index c7716def6..d94728586 100644 --- a/cold-wallet-app/lib/screens/near_key_export_screen.dart +++ b/cold-wallet-app/lib/screens/public_key_export_screen.dart @@ -4,19 +4,20 @@ import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:quantus_cold_wallet/components/ur_qr_panel.dart'; import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; -/// Shows one account's ML-DSA-65 key in NEAR's form, as an animated UR QR -/// and as selectable text with a copy control. +/// Shows one account's full ML-DSA-65 public key, as an animated UR QR and +/// as selectable text with a copy control. /// -/// The key is 1952 bytes, well past a single QR, hence the animation. The -/// `ml-dsa-65-hash:` handle under it is what NEAR lists on chain once the key -/// is added, so a user can confirm there that the key they added is this one. -/// Nothing here touches NEAR: adding the key to an account is done elsewhere, -/// with `near account add-key` or a wallet that reads this export. -class NearKeyExportScreen extends StatelessWidget { +/// A Quantus address is a hash of the key, so anything that registers the key +/// itself needs this screen. The key is a Quantus key first; NEAR is the one +/// other system that accepts ML-DSA-65 keys today, so the text form is the +/// `ml-dsa-65:` encoding NEAR reads, and the `ml-dsa-65-hash:` handle +/// is how NEAR would list the key if it were added to an account there. +/// Nothing here touches any chain: registering the key is done elsewhere. +class PublicKeyExportScreen extends StatelessWidget { final NearPublicKeyExport export; final String handle; - const NearKeyExportScreen({super.key, required this.export, required this.handle}); + const PublicKeyExportScreen({super.key, required this.export, required this.handle}); Future _copyKey(BuildContext context) async { await Clipboard.setData(ClipboardData(text: export.nearPublicKey)); @@ -24,7 +25,7 @@ class NearKeyExportScreen extends StatelessWidget { final colors = context.colorsV3; ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text('NEAR public key copied', style: context.themeTextV3.body.copyWith(color: colors.textContent)), + content: Text('Public key copied', style: context.themeTextV3.body.copyWith(color: colors.textContent)), backgroundColor: colors.bgSurface2, duration: const Duration(seconds: 2), ), @@ -37,16 +38,17 @@ class NearKeyExportScreen extends StatelessWidget { final text = context.themeTextV3; return ScaffoldBase( - appBar: const V2AppBar(title: 'NEAR Public Key'), + appBar: const V2AppBar(title: 'Public Key'), mainContent: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: 8), Text( - 'This is the account\'s key in NEAR\'s form. Add it to a NEAR account as a full access key — for now ' - 'by copying the key into the NEAR CLI, as the Quantus hot wallet does not yet read this QR — and ' - 'transactions for that account can then be reviewed and signed here.', + 'The account\'s full ML-DSA-65 public key. Your Quantus address is a hash of it; show the key itself ' + 'only where a key is registered directly. It is written in the ml-dsa-65: form NEAR reads: ' + 'to put it on a NEAR account, paste it into the NEAR CLI for now, as the Quantus hot wallet does not ' + 'yet read this QR. Transactions for that account can then be reviewed and signed here.', style: text.body.copyWith(color: colors.textMuted), textAlign: TextAlign.center, ), @@ -54,10 +56,10 @@ class NearKeyExportScreen extends StatelessWidget { UrQrPanel(payload: UrPayload(export.encode())), const SizedBox(height: 24), DetailSummaryRow.stacked( - label: 'On-chain handle', + label: 'If added to a NEAR account', value: handle, monospace: true, - note: 'How NEAR lists this key once it is added. Check it matches on the explorer.', + note: 'NEAR lists keys by this handle rather than the key itself. Check it matches on the explorer.', ), DetailSummaryRow.stacked(label: 'Quantus account', value: export.address, monospace: true), Padding( diff --git a/cold-wallet-app/lib/screens/show_key_screen.dart b/cold-wallet-app/lib/screens/show_key_screen.dart index 1e233eea4..41d6d316a 100644 --- a/cold-wallet-app/lib/screens/show_key_screen.dart +++ b/cold-wallet-app/lib/screens/show_key_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:quantus_cold_wallet/providers/wallet_providers.dart'; -import 'package:quantus_cold_wallet/screens/near_key_export_screen.dart'; +import 'package:quantus_cold_wallet/screens/public_key_export_screen.dart'; class ShowKeyScreen extends ConsumerWidget { /// The account to show; defaults to the wallet's first. @@ -55,16 +55,18 @@ class ShowKeyScreen extends ConsumerWidget { error: (_, _) => const SizedBox.shrink(), ), const SizedBox(height: 24), - if (account?.scheme == DilithiumScheme.mlDsa65) _nearSection(context, ref, address), + if (account?.scheme == DilithiumScheme.mlDsa65) _publicKeySection(context, ref, address), ], ), ), ); } - /// ML-DSA-65 is the one scheme NEAR accepts, so only those accounts offer - /// their key in NEAR's form. The key pair is derived on tap, not on build. - Widget _nearSection(BuildContext context, WidgetRef ref, String address) { + /// The full key is only useful where a key is registered directly rather + /// than by Quantus address, and ML-DSA-65 is the one scheme such a system + /// (NEAR) accepts today, so only those accounts offer it. The key pair is + /// derived on tap, not on build. + Widget _publicKeySection(BuildContext context, WidgetRef ref, String address) { final colors = context.colorsV3; final text = context.themeTextV3; return Container( @@ -73,11 +75,11 @@ class ShowKeyScreen extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text('NEAR', style: text.labelMonogram.copyWith(color: colors.accentFlare, letterSpacing: 1.2)), + Text('PUBLIC KEY', style: text.labelMonogram.copyWith(color: colors.accentFlare, letterSpacing: 1.2)), const SizedBox(height: 8), Text( - 'This key can also hold a NEAR account. Export it in NEAR\'s form to add it there; NEAR transactions ' - 'for that account are then reviewed and signed on this device.', + 'The address above is a hash of this account\'s key. Systems that register the key itself, such as ' + 'NEAR, need the full public key; transactions they send for it are then reviewed and signed here.', style: text.caption.copyWith(color: colors.textMuted), ), const SizedBox(height: 16), @@ -90,7 +92,7 @@ class ShowKeyScreen extends ConsumerWidget { Navigator.push( context, MaterialPageRoute( - builder: (_) => NearKeyExportScreen( + builder: (_) => PublicKeyExportScreen( export: NearPublicKeyExport( address: address, nearPublicKey: nearPublicKeyText(keypair: keypair), diff --git a/cold-wallet-app/test/public_key_export_screen_test.dart b/cold-wallet-app/test/public_key_export_screen_test.dart new file mode 100644 index 000000000..10b1f5ae1 --- /dev/null +++ b/cold-wallet-app/test/public_key_export_screen_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:quantus_cold_wallet/providers/ur_qr_providers.dart'; +import 'package:quantus_cold_wallet/screens/public_key_export_screen.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const address = 'qznQKhufTDfU3szAzfgCny7wMhxUN3qjEqneiRUNgC7MjSDyG'; + const key = 'ml-dsa-65:3TJcasmT6vhSWasNoHMfCe6jJ5GFAT9e8kLXkz6VrqLU'; + const handle = 'ml-dsa-65-hash:3jLEZwqD3ScQ2B6uRSuwVNGwML1XM8qqc1UBPLSdwLzJ'; + + testWidgets('presents the key as the account\'s public key, with NEAR as one place it can be registered', ( + tester, + ) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + urQrFramesProvider.overrideWith((ref, payload) => ['ur:bytes/1-2/abcd', 'ur:bytes/2-2/efgh']), + ], + child: Builder( + builder: (context) => MaterialApp( + theme: AppTheme.darkTheme(context), + home: const PublicKeyExportScreen( + export: NearPublicKeyExport(address: address, nearPublicKey: key), + handle: handle, + ), + ), + ), + ), + ); + await tester.pump(); + + expect(find.text('Public Key'), findsOneWidget); + expect(find.text('NEAR Public Key'), findsNothing); + expect(find.textContaining('full ML-DSA-65 public key'), findsOneWidget); + expect(find.textContaining('Your Quantus address is a hash of it'), findsOneWidget); + expect(find.text('IF ADDED TO A NEAR ACCOUNT'), findsOneWidget); + expect(find.text(handle), findsOneWidget); + expect(find.text('QUANTUS ACCOUNT'), findsOneWidget); + expect(find.text(address), findsOneWidget); + expect(find.text('FULL KEY'), findsOneWidget); + expect(find.text(key), findsOneWidget); + expect(find.byIcon(Icons.copy_rounded), findsOneWidget); + }); +}