This commit is contained in:
jonesmarvin8 2026-05-06 20:35:46 -04:00
parent 36a1ff319c
commit d98f13adde
6 changed files with 151 additions and 95 deletions

View File

@ -178,6 +178,33 @@ Account owned by authenticated transfer program
| `wallet token burn` | ChainIndex (key path) for holding account |
| `wallet token mint` | ChainIndices (key paths) for definition and holding public accounts |
These commands work as expected, but uses `key-path` features to use a public account via Keycard.
1. Initialize new Token
```bash
wallet token new \
--definition-key-path "m/44'/60'/0'/1/0" \
--supply-key-path "m/44'/60'/0'/1/1" \
--total-supply 100000 \
--name SNT
# Output:
Keycard PIN:
Transaction hash is 2f0ddd9ad46e1c8cde8dac4eb69ebb5d8fdf167647e421aa79900adaaa9b34d0
```
2. Initialize new token holding
```bash
wallet token init \
--definition-account-id "Public/$LEE_DEF_ID" \
--holder-key-path "m/44'/60'/0'/0/2"
# Output
Keycard PIN:
Transaction hash is d4442e32bf33efbac03672e3c5f6e181bc7e34f0911cf00ef915eeaee6787a5b
LEE holding initialized for keycard m/44'/60'/0'/0/2
```
### AMM program
| Command | Description |
|--------------------------------|---------------------------------------------------------------------|
@ -185,4 +212,6 @@ Account owned by authenticated transfer program
| `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 |
| `wallet amm remove-liquidity` | ChainIndices (key paths) for definition and holding public accounts |
These commands work as expected, but uses `key-path` features to use a public account via Keycard.

View File

@ -33,6 +33,8 @@ wallet account get --key-path "m/44'/60'/0'/0/0"
echo "=== Test: pinata claim path 0 ==="
wallet pinata claim --key-path "m/44'/60'/0'/0/0"
sleep 5
echo "=== Test: account get path 0 (after claim) ==="
wallet account get --key-path "m/44'/60'/0'/0/0"
@ -48,13 +50,6 @@ echo "=== Test: account get path 0 ==="
wallet account get --key-path "m/44'/60'/0'/0/0"
echo "=== Test: account get path 1 ==="
wallet account get --key-path "m/44'/60'/0'/0/1"
# =============================================================================
# (1) Shielded auth-transfer to an owned private account; verify decoded state.
#
# Use --to-label (ShieldedOwned path) so the wallet decodes the received note
# after sync and the balance is visible locally.
# =============================================================================
echo ""
echo "=== Test (1): Shielded auth-transfer to owned private account ==="
@ -64,6 +59,5 @@ wallet auth-transfer send --amount 2 \
--to-vpk "02a8626b0c0ad9383c5678dad48c3969b4174fb377cdb03a6259648032c774cec8"
echo "Shielded auth-transfer sent"
# TODO: add a time delay here
sleep 5
wallet account get --key-path "m/44'/60'/0'/0/0"

View File

@ -1,17 +1,15 @@
source venv/bin/activate
export KEYCARD_PIN=111111
# =============================================================================
# (2) Initialize token definitions + initial supply holdings for LEZ and LEE.
# All without keycard.
# =============================================================================
echo ""
echo "=== Test (2): Create LEZ and LEE token definitions (without keycard) ==="
echo "=== Create LEZ and LEE token definitions (without keycard) ==="
wallet account new public --label lez-def 2>/dev/null || true
wallet account new public --label lez-supply 2>/dev/null || true
wallet account new public --label lee-def 2>/dev/null || true
wallet account new public --label lee-supply 2>/dev/null || true
wallet account new public --label lez-def
wallet account new public --label lez-supply
wallet account new public --label lee-def
wallet account new public --label lee-supply
sleep 5
LEZ_DEF_ID=$(wallet account id --account-label lez-def)
LEE_DEF_ID=$(wallet account id --account-label lee-def)
@ -31,14 +29,12 @@ wallet token new \
echo "LEE token created"
# =============================================================================
# (3) Initialize LEE token holding accounts:
# Initialize LEE token holding accounts:
# - two public keycard holders (paths 2 and 3)
# - one private holder (without keycard)
#
# token init is idempotent: skips if the holder already has token data.
# =============================================================================
echo ""
echo "=== Test (3): Initialize LEE token holding accounts ==="
echo "=== Initialize LEE/LEZ token holding accounts ==="
wallet token init \
--definition-account-id "Public/$LEE_DEF_ID" \
@ -50,6 +46,7 @@ wallet token init \
--holder-key-path "m/44'/60'/0'/0/3"
echo "LEE holding initialized for keycard m/44'/60'/0'/0/3"
# For shielded transfer
wallet account new private --label lee-priv-holder 2>/dev/null || true
wallet token init \
--definition-account-id "Public/$LEE_DEF_ID" \
@ -71,17 +68,20 @@ wallet token send \
--amount 5000
echo "Transferred 5000 LEE → keycard path 3"
sleep 5
echo "Keycard path 2 LEE state (balance should be 5000):"
wallet account get --key-path "m/44'/60'/0'/0/2"
echo "Keycard path 3 LEE state (balance should be 5000):"
wallet account get --key-path "m/44'/60'/0'/0/3"
# =============================================================================
# (4) Shielded (public → private) LEE transfer from keycard holding to the
# private LEE holding account.
# Shielded (public → private) LEE transfer from keycard holding to the
# private LEE holding account.
# =============================================================================
echo ""
echo "=== Test (4): Shielded transfer keycard LEE holding → private LEE holding ==="
echo "=== Test Shielded transfer keycard LEE holding → private LEE holding ==="
wallet token send \
--from-key-path "m/44'/60'/0'/0/2" \
@ -94,10 +94,10 @@ echo "Private LEE holder state (balance should be 500):"
wallet account get --account-label lee-priv-holder
# =============================================================================
# (5) Create AMM pool for LEZ/LEE (without keycard)
# Create AMM pool for LEZ/LEE (without keycard)
# =============================================================================
echo ""
echo "=== Test (5): Create AMM pool for LEZ/LEE (without keycard) ==="
echo "=== Test Create AMM pool for LEZ/LEE (without keycard) ==="
wallet account new public --label amm-lp-lez-holding 2>/dev/null || true
wallet account new public --label amm-lp-lee-holding 2>/dev/null || true
@ -122,7 +122,7 @@ wallet amm new \
echo "AMM pool created for LEZ/LEE"
# =============================================================================
# (6) Swaps, add liquidity, remove liquidity using keycard holding accounts.
# Swaps, add liquidity, remove liquidity using keycard holding accounts.
#
# Path layout:
# path 2 → LEE holding (4500 LEE after step 4)
@ -130,7 +130,7 @@ echo "AMM pool created for LEZ/LEE"
# path 4 → fresh; initialized below as LEZ holding (receives swapped LEZ)
# =============================================================================
echo ""
echo "=== Test (6a): Initialize LEZ holding for keycard path 4 (swap output) ==="
echo "=== Test Initialize LEZ holding for keycard path 4 (swap output) ==="
wallet token init \
--definition-account-id "Public/$LEZ_DEF_ID" \
--holder-key-path "m/44'/60'/0'/0/4"
@ -145,18 +145,16 @@ echo "Path 2: $PATH2_ID Path 3: $PATH3_ID Path 4: $PATH4_ID"
echo "LEE def ID: $LEE_DEF_ID"
echo ""
echo "=== Test (6b): Swap LEE → LEZ (path 2 sells LEE, path 4 receives LEZ) ==="
# user-holding-b (path 2) is the input (LEE); user-holding-a (path 4) receives LEZ.
# --key-path signs for the input account (path 2).
echo "=== Swap LEE → LEZ (path 2 sells LEE, path 4 receives LEZ) ==="
wallet amm swap-exact-input \
--user-holding-a "Public/$PATH4_ID" \
--user-holding-b "Public/$PATH2_ID" \
--amount-in 500 \
--user-holding-a-key-path "m/44'/60'/0'/0/4" \
--user-holding-b-key-path "m/44'/60'/0'/0/2" \
--amount-in 100 \
--min-amount-out 1 \
--token-definition "$LEE_DEF_ID" \
--key-path "m/44'/60'/0'/0/2"
echo "Swap LEE→LEZ complete via keycard"
echo "Path 4 (LEZ) state:"
wallet account get --key-path "m/44'/60'/0'/0/4"
echo "Path 2 (LEE) state:"
@ -185,6 +183,6 @@ wallet amm remove-liquidity \
echo "Remove liquidity complete"
echo ""
echo "=== All keycard tests finished ==="

View File

@ -30,8 +30,7 @@ pub enum AmmProgramAgnosticSubcommand {
#[arg(
long,
conflicts_with = "user_holding_a",
conflicts_with = "user_holding_a_key_path",
required_unless_present_any = ["user_holding_a_label", "user_holding_a_key_path"]
conflicts_with = "user_holding_a_key_path"
)]
user_holding_a_label: Option<String>,
/// Key path for user holding A (uses Keycard, alternative to --user-holding-a/label).
@ -67,7 +66,8 @@ pub enum AmmProgramAgnosticSubcommand {
#[arg(
long,
conflicts_with = "user_holding_lp_label",
conflicts_with = "user_holding_lp_key_path"
conflicts_with = "user_holding_lp_key_path",
required_unless_present_any = ["user_holding_lp_label", "user_holding_lp_key_path"]
)]
user_holding_lp: Option<String>,
/// User holding LP account label (alternative to --user-holding-lp).
@ -128,11 +128,19 @@ pub enum AmmProgramAgnosticSubcommand {
/// `token_definition` - valid 32 byte base58 string WITHOUT privacy prefix.
#[arg(long)]
token_definition: String,
/// Key path for the input token's holding account (uses Keycard).
#[arg(long, conflicts_with = "user_holding_a")]
/// Key path for user token's holding account fpr Token A (uses Keycard).
#[arg(
long,
conflicts_with = "user_holding_a",
conflicts_with = "user_holding_a_label"
)]
user_holding_a_key_path: Option<String>,
/// Key path for the input token's holding account (uses Keycard).
#[arg(long, conflicts_with = "user_holding_b")]
/// Key path for user token's holding account fpr Token B (uses Keycard).
#[arg(
long,
conflicts_with = "user_holding_b",
conflicts_with = "user_holding_b_label"
)]
user_holding_b_key_path: Option<String>,
},
/// Swap specifying exact output amount.

View File

@ -117,7 +117,7 @@ pub enum TokenProgramAgnosticSubcommand {
#[arg(long, conflicts_with = "from", conflicts_with = "from_key_path")]
from_label: Option<String>,
/// to - valid 32 byte base58 string with privacy prefix.
#[arg(long, conflicts_with = "to_label")]
#[arg(long, conflicts_with = "to_label", conflicts_with = "to_key_path")]
to: Option<String>,
/// To account label (alternative to --to).
#[arg(long, conflicts_with = "to")]
@ -136,20 +136,10 @@ pub enum TokenProgramAgnosticSubcommand {
#[arg(long)]
amount: u128,
/// `from_key_path` (alternative to --from) uses Keycard.
#[arg(
long,
conflicts_with = "from",
conflicts_with = "from",
conflicts_with = "from_label"
)]
#[arg(long, conflicts_with = "from", conflicts_with = "from_label")]
from_key_path: Option<String>,
/// `to_key_path` (alternative to --to) uses Keycard.
#[arg(
long,
conflicts_with = "to",
conflicts_with = "to",
conflicts_with = "to_label"
)]
#[arg(long, conflicts_with = "to", conflicts_with = "to_label")]
to_key_path: Option<String>,
},
/// Burn tokens on `holder`, modify `definition`.

View File

@ -197,14 +197,46 @@ impl Token<'_> {
let instruction = Instruction::Transfer {
amount_to_transfer: amount,
};
// Only the sender authorises a token Transfer — the recipient holding must already be
// initialised (no recipient signature required, matching the burn pattern).
let nonces = self
let mut nonces = self
.0
.get_accounts_nonces(vec![sender_account_id])
.await
.map_err(ExecutionFailureKind::SequencerError)?;
let private_keys = if sender_key_path.is_none() {
let mut private_keys = Vec::new();
let sender_sk = self
.0
.storage
.user_data
.get_pub_account_signing_key(sender_account_id)
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
private_keys.push(sender_sk);
if let Some(recipient_sk) = self
.0
.storage
.user_data
.get_pub_account_signing_key(recipient_account_id)
{
private_keys.push(recipient_sk);
let recipient_nonces = self
.0
.get_accounts_nonces(vec![recipient_account_id])
.await
.map_err(ExecutionFailureKind::SequencerError)?;
nonces.extend(recipient_nonces);
} else {
println!(
"Receiver's account ({recipient_account_id}) private key not found in wallet. Proceeding with only sender's key."
);
}
private_keys
} else {
Vec::new()
};
let message = nssa::public_transaction::Message::try_new(
program_id,
account_ids,
@ -227,13 +259,7 @@ impl Token<'_> {
WitnessSet::from_list(&message, &[signature], &[public_key])
.map_err(ExecutionFailureKind::TransactionBuildError)?
} else {
let sender_sk = self
.0
.storage
.user_data
.get_pub_account_signing_key(sender_account_id)
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
nssa::public_transaction::WitnessSet::for_message(&message, &[sender_sk])
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys)
};
let tx = nssa::PublicTransaction::new(message, witness_set);
@ -660,24 +686,38 @@ impl Token<'_> {
.await
.map_err(ExecutionFailureKind::SequencerError)?;
if self
.0
.storage
.user_data
.get_pub_account_signing_key(holder_account_id)
.is_some()
{
let recipient_nonces = self
let private_keys = if definition_key_path.is_none() {
let mut private_keys = Vec::new();
let definition_sk = self
.0
.get_accounts_nonces(vec![holder_account_id])
.await
.map_err(ExecutionFailureKind::SequencerError)?;
nonces.extend(recipient_nonces);
.storage
.user_data
.get_pub_account_signing_key(definition_account_id)
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
private_keys.push(definition_sk);
if let Some(holder_sk) = self
.0
.storage
.user_data
.get_pub_account_signing_key(holder_account_id)
{
private_keys.push(holder_sk);
let holder_nonce: Vec<nssa_core::account::Nonce> = self
.0
.get_accounts_nonces(vec![holder_account_id])
.await
.map_err(ExecutionFailureKind::SequencerError)?;
nonces.extend(holder_nonce);
} else {
println!(
"Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key."
);
}
private_keys
} else {
println!(
"Holder's account ({holder_account_id}) private key not found in wallet. Proceeding with only definition's key."
);
}
Vec::new()
};
let message = nssa::public_transaction::Message::try_new(
Program::token().id(),
@ -687,24 +727,21 @@ impl Token<'_> {
)
.unwrap();
let msg_hash = message.hash();
let witness_set = if let Some(kp) = definition_key_path {
let witness_set = if let Some(definition_key_path) = definition_key_path {
let pin = crate::helperfunctions::read_pin().map_err(|e| {
ExecutionFailureKind::KeycardError(pyo3::PyErr::new::<PyRuntimeError, _>(
e.to_string(),
))
})?;
let (sig, pk) = KeycardWallet::sign_message_for_path_with_connect(&pin, kp, &msg_hash)?;
nssa::public_transaction::WitnessSet::from_list(&message, &[sig], &[pk])
let (signature, public_key) = KeycardWallet::sign_message_for_path_with_connect(
&pin,
definition_key_path,
&message.hash(),
)?;
WitnessSet::from_list(&message, &[signature], &[public_key])
.map_err(ExecutionFailureKind::TransactionBuildError)?
} else {
let signing_key = self
.0
.storage
.user_data
.get_pub_account_signing_key(definition_account_id)
.ok_or(ExecutionFailureKind::KeyNotFoundError)?;
nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key])
nssa::public_transaction::WitnessSet::for_message(&message, &private_keys)
};
let tx = nssa::PublicTransaction::new(message, witness_set);