From 36a1ff319cc3402a6a5bcc02ab6d9c5067c854aa Mon Sep 17 00:00:00 2001 From: jonesmarvin8 <83104039+jonesmarvin8@users.noreply.github.com> Date: Tue, 5 May 2026 19:03:01 -0400 Subject: [PATCH] some fixes and setup notes --- docs/LEZ testnet v0.1 tutorials/keycard.md | 188 +++++++++++++++++++++ integration_tests/tests/amm.rs | 11 +- keycard_tests_2.sh | 6 +- wallet/src/cli/programs/amm.rs | 98 ++++++++--- wallet/src/program_facades/amm.rs | 40 +++-- 5 files changed, 306 insertions(+), 37 deletions(-) create mode 100644 docs/LEZ testnet v0.1 tutorials/keycard.md diff --git a/docs/LEZ testnet v0.1 tutorials/keycard.md b/docs/LEZ testnet v0.1 tutorials/keycard.md new file mode 100644 index 00000000..a6634313 --- /dev/null +++ b/docs/LEZ testnet v0.1 tutorials/keycard.md @@ -0,0 +1,188 @@ +This tutorial walks you through using Keycard with Wallet CLI. Keycard is optional hardware that can offer enhance security to a LEZ wallet. A LEZ wallet that utilizes Keycard does not store any secret keys for public accounts (eventually, this will extend to private accounts). Instead, Wallet CLI retrieves the appropriate public keys and signatures from Keycard. + + +## Keycard Setup + +### Required hardware +- Keycard (Blank) - a Keycard, directly, from Keycard.tech cannot (currently) be updated to support LEE. +- Smartcard reader + +### Firmware installation +Installation: + +1. Install `math.cap` on your keycard; this process only needs to be done once. (TODO: can this cap file be shared externally?) + - `java -jar gp.jar --key c212e073ff8b4bbfaff4de8ab655221f --load math.cap` +2. Install `keycard-desktop` from [github](https://github.com/choppu/keycard-desktop) + - Keycard Desktop is used to install the LEE key protocol to a blank keycard. + - Select (Re)Install Applet and upload the cap. (TODO: double check that we can upload to repo) + ![keycard-desktop.png](keycard-desktop.png) + +## Wallet with Keycard +Keycard functionality is available to Wallet CLI by setting up the following Python virtual environment: + +```bash +# Setup virtual environment. +python3 -m venv venv +source venv/bin/activate +python3 -m pip install pyscard +python3 -m pip install mnemonic +python3 -m pip install ecdsa +python3 -m pip install pyaes + +# Install appropriate version of `keycard-py`. +cd python +# Need to use local version till fix applet +git clone --branch lee-schnorr --single-branch https://github.com/bitgamma/keycard-py.git +cd keycard-py +python3 -m venv venv +source venv/bin/activate +pip install -e . +``` + +**Important**: Keycard wallet commands only work within the virtual environment. +```bash +# In the root of LEE repo: +source venv/bin/activate +``` + +## Keycard Commands + +### Keycard + +| Command | Description | +|------------------------|-------------------------------------------------------------------------| +| `wallet keycard available` | Checks whether Keycard is accessible | +| `wallet keycard load` | Loads a new mnemonic phrase onto Keycard | +| `wallet keycard get-private-keys` | Retrieves private account keys (nsk, vsk) given a ChainIndex | +| `wallet help` | Help | + +### 1. Check keycard availability +```bash +wallet keycard available + +# Output: +✅ Keycard is available. +``` + +### 2. Load a new mnemonic phrase +```bash +wallet keycard load --mnemonic "fashion degree mountain wool question damp current pond grow dolphin chronic then" + +# Output: +Keycard PIN: # Enter Keycard pin +✅ Keycard is now connected to wallet. +``` + +### 3. Ger private key +```bash +wallet keycard get-private-keys --key-path "m/44'/60'/0'/0/0" + +# Output: +Keycard PIN: # Enter Keycard pin +nsk: 55e505bf925e536c843a12ebc08c41ca5f4761eeeb7fa33725f0b44e6f1ac2e4 +vsk: 30f798893977a7b7263d1f77abf58e11e014428c92030d6a02fe363cceb41ffa +``` +### Pinata (testnet) +| Command | Description | +|------------------------|---------------------------------------------------------------------| +| `wallet pinata claim` | Accepts ChainIndex (key path) for public account to send pinata reward to| + +- See example in Authenicated-transfers examples. + +### Authenticated-transfer program + +| Command | Description | +|------------------------|---------------------------------------------------------------------| +| `wallet auth-transfer init` | Accepts ChainIndex (key path) for public account to initialize| +| `wallet auth-transfer send` | Accepts ChainIndices (key paths) for `from` and `to` | + +1. Initialize public account. +```bash +wallet auth-transfer init --key-path "m/44'/60'/0'/0/0" + +#Output +Keycard PIN: # Enter Keycard pin +Transaction hash is 49c16940493e1618c393645c1211b5c793d405838221c29ac6562a8a4b11c5a7 +Transaction data is Public(PublicTransaction { message: Message { program_id: "adbf67b01ded1e29c7a0b7b1b580e6ad4166a8fd52b57bf629fe78d4c779a48a", account_ids: [Fh8d1HsqEUDzwg1vc1E9123nRpEGHJwwdqtiWu73JHPP], nonces: [Nonce(0)], instruction_data: [0, 0, 0, 0] }, witness_set: WitnessSet { signatures_and_public_keys: [(9b43433cce6f0c5c268e408c0e300b8767c11f462a2fbea50b22b3f52ddeea86b5c3e959c1d3625603e4d22b8ccff62caa7819e28761cec9bbfb32b87a9e0c81, 0a5873812f910609da22d8aef918632677f3208faa3cec2b692f3e4c37ffd11d)] } }) +Stored persistent accounts at /home/mara/.nssa/wallet/storage.json +``` + +2. Fund initialized public account with Pinata reward. +```bash +wallet pinata claim --key-path "m/44'/60'/0'/0/0" + +#Output: +Keycard PIN: # Enter Keycard pin +Computing solution for pinata... +Found solution 989106 in 33.739525ms +Transaction hash is fd320c01f5469e62d2486afa1d9d5be39afcca0cd01d1575905b7acd95cf6397 +Transaction data is Public(PublicTransaction { message: Message { program_id: "d68109238654ce4c7af77ba2cdeb3f3d8eb3ea1fb563b9d5b15c0f081c3d6f40", account_ids: [EfQhKQAkX2FJiwNii2WFQsGndjvF1Mzd7RuVe7QdPLw7, Fh8d1HsqEUDzwg1vc1E9123nRpEGHJwwdqtiWu73JHPP], nonces: [], instruction_data: [989106, 0, 0, 0] }, witness_set: WitnessSet { signatures_and_public_keys: [] } }) +``` + +3. Initialize new public account and send funds. +```bash +# Initialize new Keycard public account. +wallet auth-transfer init --key-path "m/44'/60'/0'/0/1" + +# Output: +Keycard PIN: # Enter Keycard pin +Transaction hash is a801bd61c0acc04917fb61e8e27673591df368ab0542c68a5cb1cf2272744d8e +Transaction data is Public(PublicTransaction { message: Message { program_id: "adbf67b01ded1e29c7a0b7b1b580e6ad4166a8fd52b57bf629fe78d4c779a48a", account_ids: [7ZMZuc3FgzHj3pSggGP1kbeZRCdwD8L4P8Wg9N2qsYcB], nonces: [Nonce(0)], instruction_data: [0, 0, 0, 0] }, witness_set: WitnessSet { signatures_and_public_keys: [(4470c76742e3a965fd07c5d5510a25a8e9b553f922df40ec5c4585e2b765e4c15056ee7485440b49762f92654bb7f4bba0565cbc7e70154ae475c990052cb830, 1c53c6a6c5e552fd739b3812f04381c0e502278fe851efea491cfd2f560cd25d)] } }) +Stored persistent accounts at /home/mara/.nssa/wallet/storage.json + +# Send native tokens from one account to the other. +wallet auth-transfer send --amount 40 \ + --from-key-path "m/44'/60'/0'/0/0" \ + --to-key-path "m/44'/60'/0'/0/1" + +# Output: +Keycard PIN: # Enter Keycard pin +Transaction hash is 1a9764ab20763dcc1ffb51c6e9badd5a6316a773759032ca48e0eee59caaf488 +Transaction data is Public(PublicTransaction { message: Message { program_id: "adbf67b01ded1e29c7a0b7b1b580e6ad4166a8fd52b57bf629fe78d4c779a48a", account_ids: [Fh8d1HsqEUDzwg1vc1E9123nRpEGHJwwdqtiWu73JHPP, 7ZMZuc3FgzHj3pSggGP1kbeZRCdwD8L4P8Wg9N2qsYcB], nonces: [Nonce(1), Nonce(1)], instruction_data: [40, 0, 0, 0] }, witness_set: WitnessSet { signatures_and_public_keys: [(073fc99121c9d95de120b15057fdd922de2bb059db8382674f600cd2047aa97d1d2cad09becd8e05014a3704916f41e29eedf002649bffe7864f431a75d5f6a2, 0a5873812f910609da22d8aef918632677f3208faa3cec2b692f3e4c37ffd11d), (e6feaafd2de792be0f7f94312c3a97285e7b1f806388a0c10f7a5123a3d0389363fe35d0d56f1af21a0ead999a015d89479c004cc4dd33078efb17611b50b590, 1c53c6a6c5e552fd739b3812f04381c0e502278fe851efea491cfd2f560cd25d)] } }) +``` + +4. Shielded transfer: +```bash +wallet auth-transfer send --amount 2 \ + --from-key-path "m/44'/60'/0'/0/0" \ + --to-npk "55204e2934045b044f06d8222b454d46b54788f33c7dec4f6733d441703bb0e6" \ + --to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8" + +# Output: +Keycard PIN: # Enter Keycard pin +Transaction hash is 8ad4b2dc5ab2c08bb6eefca6ec9b18151fa4452cd7e2a636c2fb158ecb46aef6 +Stored persistent accounts at /home/mara/.nssa/wallet/storage.json +Shielded auth-transfer sent +``` + +### Account +| Command | Description | +|------------------------|---------------------------------------------------------------------| +| `wallet account get` | Get public account data given its ChainIndex (key path) | + +```bash +wallet account get --key-path "m/44'/60'/0'/0/0" + +# Output: +Keycard PIN: # Enter Keycard pin +Account owned by authenticated transfer program +{"balance":108,"program_owner":"ChEp4BuCdGzJDHWoG1PTZLfZBbPxhYzstVMdakxym6bb","data":"","nonce":3} +``` + +### Token program +| Command | Description | +|------------------------|---------------------------------------------------------------------| +| `wallet token new` | ChainIndices (key paths) provided for definition and supply | +| `wallet token init` | ChaindIndex (key path) to initialize Keycard public account | +| `wallet token send` | ChainIndices (key paths) for `from` and `to` public accounts | +| `wallet token burn` | ChainIndex (key path) for holding account | +| `wallet token mint` | ChainIndices (key paths) for definition and holding public accounts | + +### AMM program +| Command | Description | +|--------------------------------|---------------------------------------------------------------------| +| `wallet amm new` | ChainIndices (key paths) provided for definition and supply | +| `wallet amm swap-exact-input` | ChaindIndex (key path) to initialize Keycard public account | +| `wallet amm swap-exact-output` | ChainIndices (key paths) for `from` and `to` public accounts | +| `wallet amm add-liquidity` | ChainIndex (key path) for holding account | +| `wallet amm reemove-liquidity` | ChainIndices (key paths) for definition and holding public accounts | \ No newline at end of file diff --git a/integration_tests/tests/amm.rs b/integration_tests/tests/amm.rs index 475d748a..ab354a10 100644 --- a/integration_tests/tests/amm.rs +++ b/integration_tests/tests/amm.rs @@ -211,6 +211,7 @@ async fn amm_public() -> Result<()> { balance_b: 3, user_holding_a_key_path: None, user_holding_b_key_path: None, + user_holding_lp_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; @@ -256,7 +257,8 @@ async fn amm_public() -> Result<()> { amount_in: 2, min_amount_out: 1, token_definition: definition_account_id_1.to_string(), - key_path: None, + user_holding_a_key_path: None, + user_holding_b_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; @@ -302,7 +304,8 @@ async fn amm_public() -> Result<()> { amount_in: 2, min_amount_out: 1, token_definition: definition_account_id_2.to_string(), - key_path: None, + user_holding_a_key_path: None, + user_holding_b_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; @@ -352,6 +355,7 @@ async fn amm_public() -> Result<()> { max_amount_b: 2, user_holding_a_key_path: None, user_holding_b_key_path: None, + user_holding_lp_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; @@ -399,6 +403,8 @@ async fn amm_public() -> Result<()> { balance_lp: 2, min_amount_a: 1, min_amount_b: 1, + user_holding_a_key_path: None, + user_holding_b_key_path: None, user_holding_lp_key_path: None, }; @@ -618,6 +624,7 @@ async fn amm_new_pool_using_labels() -> Result<()> { balance_b: 3, user_holding_a_key_path: None, user_holding_b_key_path: None, + user_holding_lp_key_path: None, }; wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; diff --git a/keycard_tests_2.sh b/keycard_tests_2.sh index 54be3a4c..513583bb 100644 --- a/keycard_tests_2.sh +++ b/keycard_tests_2.sh @@ -167,7 +167,7 @@ echo "=== Test (6c): Add liquidity (path 4 LEZ + path 3 LEE) ===" wallet amm add-liquidity \ --user-holding-a-key-path "m/44'/60'/0'/0/4" \ --user-holding-b-key-path "m/44'/60'/0'/0/3" \ - --user-holding-lp-label amm-lp-lp-holding \ + --user-holding-lp-label "m/44'/60'/0'/0/5" \ --min-amount-lp 1 \ --max-amount-a 200 \ --max-amount-b 200 @@ -176,8 +176,8 @@ echo "Add liquidity complete via keycard" echo "" echo "=== Test (6d): Remove liquidity (LP from amm-lp-lp-holding) ===" wallet amm remove-liquidity \ - --user-holding-a-label amm-lp-lez-holding \ - --user-holding-b-label amm-lp-lee-holding \ + --user-holding-a-label "m/44'/60'/0'/0/4"\ + --user-holding-b-label "m/44'/60'/0'/0/3" \ --user-holding-lp-label amm-lp-lp-holding \ --balance-lp 1000 \ --min-amount-a 1 \ diff --git a/wallet/src/cli/programs/amm.rs b/wallet/src/cli/programs/amm.rs index b65923dc..9e0538e6 100644 --- a/wallet/src/cli/programs/amm.rs +++ b/wallet/src/cli/programs/amm.rs @@ -30,7 +30,8 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_a", - conflicts_with = "user_holding_a_key_path" + conflicts_with = "user_holding_a_key_path", + required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a_label: Option, /// Key path for user holding A (uses Keycard, alternative to --user-holding-a/label). @@ -66,12 +67,23 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_lp_label", - required_unless_present = "user_holding_lp_label" + conflicts_with = "user_holding_lp_key_path" )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). - #[arg(long, conflicts_with = "user_holding_lp")] + #[arg( + long, + conflicts_with = "user_holding_lp", + conflicts_with = "user_holding_lp_key_path" + )] user_holding_lp_label: Option, + /// User holding LP key path (alternative to --user-holding-lp) for Keycard. + #[arg( + long, + conflicts_with = "user_holding_lp", + conflicts_with = "user_holding_lp_label" + )] + user_holding_lp_key_path: Option, #[arg(long)] balance_a: u128, #[arg(long)] @@ -87,7 +99,8 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_a_label", - required_unless_present = "user_holding_a_label" + conflicts_with = "user_holding_a_key_path", + required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"] )] user_holding_a: Option, /// User holding A account label (alternative to --user-holding-a). @@ -97,11 +110,16 @@ pub enum AmmProgramAgnosticSubcommand { #[arg( long, conflicts_with = "user_holding_b_label", - required_unless_present = "user_holding_b_label" + conflicts_with = "user_holding_b_key_path", + required_unless_present_any = ["user_holding_b_label", "user_holding_b_key_path"] )] user_holding_b: Option, /// User holding B account label (alternative to --user-holding-b). - #[arg(long, conflicts_with = "user_holding_b")] + #[arg( + long, + conflicts_with = "user_holding_b", + conflicts_with = "user_holding_b_key_path" + )] user_holding_b_label: Option, #[arg(long)] amount_in: u128, @@ -111,8 +129,11 @@ pub enum AmmProgramAgnosticSubcommand { #[arg(long)] token_definition: String, /// Key path for the input token's holding account (uses Keycard). - #[arg(long)] - key_path: Option, + #[arg(long, conflicts_with = "user_holding_a")] + user_holding_a_key_path: Option, + /// Key path for the input token's holding account (uses Keycard). + #[arg(long, conflicts_with = "user_holding_b")] + user_holding_b_key_path: Option, }, /// Swap specifying exact output amount. /// @@ -134,8 +155,11 @@ pub enum AmmProgramAgnosticSubcommand { #[arg(long)] token_definition: String, /// Key path for the input token's holding account (uses Keycard). - #[arg(long)] - key_path: Option, + #[arg(long, conflicts_with = "user_holding_a")] + user_holding_a_key_path: Option, + /// Key path for the input token's holding account (uses Keycard). + #[arg(long, conflicts_with = "user_holding_b")] + user_holding_b_key_path: Option, }, /// Add liquidity. /// @@ -195,8 +219,18 @@ pub enum AmmProgramAgnosticSubcommand { )] user_holding_lp: Option, /// User holding LP account label (alternative to --user-holding-lp). - #[arg(long, conflicts_with = "user_holding_lp")] + #[arg( + long, + conflicts_with = "user_holding_lp", + conflicts_with = "user_holding_lp_key_path" + )] user_holding_lp_label: Option, + #[arg( + long, + conflicts_with = "user_holding_lp", + conflicts_with = "user_holding_lp_label" + )] + user_holding_lp_key_path: Option, #[arg(long)] min_amount_lp: u128, #[arg(long)] @@ -220,6 +254,13 @@ pub enum AmmProgramAgnosticSubcommand { /// User holding A account label (alternative to --user-holding-a). #[arg(long, conflicts_with = "user_holding_a")] user_holding_a_label: Option, + /// Key path for user holding a (uses Keycard). + #[arg( + long, + conflicts_with = "user_holding_a", + conflicts_with = "user_holding_a_label" + )] + user_holding_a_key_path: Option, /// `user_holding_b` - valid 32 byte base58 string with privacy prefix. #[arg( long, @@ -230,6 +271,13 @@ pub enum AmmProgramAgnosticSubcommand { /// User holding B account label (alternative to --user-holding-b). #[arg(long, conflicts_with = "user_holding_b")] user_holding_b_label: Option, + /// Key path for user holding B (uses Keycard). + #[arg( + long, + conflicts_with = "user_holding_b", + conflicts_with = "user_holding_b_label" + )] + user_holding_b_key_path: Option, /// `user_holding_lp` - valid 32 byte base58 string with privacy prefix. #[arg( long, @@ -276,6 +324,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_b_key_path, user_holding_lp, user_holding_lp_label, + user_holding_lp_key_path, balance_a, balance_b, } => { @@ -298,7 +347,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -344,26 +393,27 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { Self::SwapExactInput { user_holding_a, user_holding_a_label, + user_holding_a_key_path, user_holding_b, user_holding_b_label, + user_holding_b_key_path, amount_in, min_amount_out, token_definition, - key_path, } => { let user_holding_a = resolve_id_or_label( user_holding_a, user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_b_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -382,7 +432,8 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { amount_in, min_amount_out, token_definition.parse()?, - key_path.as_deref(), + user_holding_a_key_path.as_deref(), + user_holding_b_key_path.as_deref(), ) .await?; @@ -400,7 +451,8 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { exact_amount_out, max_amount_in, token_definition, - key_path, + user_holding_a_key_path, + user_holding_b_key_path, } => { let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -419,7 +471,8 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { exact_amount_out, max_amount_in, token_definition.parse()?, - key_path.as_deref(), + user_holding_a_key_path.as_deref(), + user_holding_b_key_path.as_deref(), ) .await?; @@ -440,6 +493,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_b_key_path, user_holding_lp, user_holding_lp_label, + user_holding_lp_key_path, min_amount_lp, max_amount_a, max_amount_b, @@ -463,7 +517,7 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_lp_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_lp_key_path.as_deref(), )?; let (user_holding_a, user_holding_a_privacy) = parse_addr_with_privacy_prefix(&user_holding_a)?; @@ -510,8 +564,10 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { Self::RemoveLiquidity { user_holding_a, user_holding_a_label, + user_holding_a_key_path, user_holding_b, user_holding_b_label, + user_holding_b_key_path, user_holding_lp, user_holding_lp_label, user_holding_lp_key_path, @@ -524,14 +580,14 @@ impl WalletSubcommand for AmmProgramAgnosticSubcommand { user_holding_a_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_a_key_path.as_deref(), )?; let user_holding_b = resolve_id_or_label( user_holding_b, user_holding_b_label, &wallet_core.storage.labels, &wallet_core.storage.user_data, - None, + user_holding_b_key_path.as_deref(), )?; let user_holding_lp = resolve_id_or_label( user_holding_lp, diff --git a/wallet/src/program_facades/amm.rs b/wallet/src/program_facades/amm.rs index 3a774490..36f7b5ca 100644 --- a/wallet/src/program_facades/amm.rs +++ b/wallet/src/program_facades/amm.rs @@ -168,6 +168,7 @@ impl Amm<'_> { .await?) } + #[expect(clippy::too_many_arguments, reason = "To fix later")] pub async fn send_swap_exact_input( &self, user_holding_a: AccountId, @@ -175,7 +176,8 @@ impl Amm<'_> { swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, - key_path: Option<&str>, + user_holding_a_key_path: Option<&str>, + user_holding_b_key_path: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::SwapExactInput { swap_amount_in, @@ -241,17 +243,22 @@ impl Amm<'_> { .unwrap(); let msg_hash = message.hash(); - let witness_set = if let Some(kp) = key_path { + let witness_set = if let (Some(kp_a), Some(kp_b)) = + (user_holding_a_key_path, user_holding_b_key_path) + { let pin = crate::helperfunctions::read_pin().map_err(|e| { ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< pyo3::exceptions::PyRuntimeError, _, >(e.to_string())) })?; - let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( - &pin, kp, &msg_hash, + let (sig1, pk1) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + &pin, kp_a, &msg_hash, )?; - nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) + let (sig2, pk2) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + &pin, kp_b, &msg_hash, + )?; + nssa::public_transaction::WitnessSet::from_list(&message, &[sig1, sig2], &[pk1, pk2]) .map_err(ExecutionFailureKind::TransactionBuildError)? } else { let signing_key = self @@ -272,6 +279,7 @@ impl Amm<'_> { .await?) } + #[expect(clippy::too_many_arguments, reason = "To fix later")] pub async fn send_swap_exact_output( &self, user_holding_a: AccountId, @@ -279,7 +287,8 @@ impl Amm<'_> { exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, - key_path: Option<&str>, + user_holding_a_key_path: Option<&str>, + user_holding_b_key_path: Option<&str>, ) -> Result { let instruction = amm_core::Instruction::SwapExactOutput { exact_amount_out, @@ -345,18 +354,27 @@ impl Amm<'_> { .unwrap(); let msg_hash = message.hash(); - let witness_set = if let Some(kp) = key_path { + let witness_set = if let (Some(kp_a), Some(kp_b)) = + (user_holding_a_key_path, user_holding_b_key_path) + { let pin = crate::helperfunctions::read_pin().map_err(|e| { ExecutionFailureKind::KeycardError(pyo3::PyErr::new::< pyo3::exceptions::PyRuntimeError, _, >(e.to_string())) })?; - let (sig, pk) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( - &pin, kp, &msg_hash, + let (sig_1, pk_1) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + &pin, kp_a, &msg_hash, )?; - nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk]) - .map_err(ExecutionFailureKind::TransactionBuildError)? + let (sig_2, pk_2) = keycard_wallet::KeycardWallet::sign_message_for_path_with_connect( + &pin, kp_b, &msg_hash, + )?; + nssa::public_transaction::WitnessSet::from_list( + &message, + &[sig_1, sig_2], + &[pk_1, pk_2], + ) + .map_err(ExecutionFailureKind::TransactionBuildError)? } else { let signing_key = self .0