Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions mobile/packages/cardano-wallet/common/signatureUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ const getRequiredSigners = async (
})

const getAddressAddressing = (bech32Address: string) => {
// Check if wallet actually controls this address (payment key)
const addressBranded = Branded.asAddress(bech32Address)
const internalIndex = wallet.internalAddresses().indexOf(addressBranded)
const externalIndex = wallet.externalAddresses().indexOf(addressBranded)
const walletControlsAddress = internalIndex !== -1 || externalIndex !== -1

// If wallet doesn't control the address and we're in strict mode, return null
if (!walletControlsAddress && !partial) {
return null
}

// If wallet doesn't control the address, don't return addressing even in partial mode
// This prevents signing for addresses we don't control (e.g., Minswap payment key + our staking key)
if (!walletControlsAddress) {
return null
}

const path = getDerivationPathForAddress(
bech32Address,
wallet,
Expand Down
41 changes: 24 additions & 17 deletions mobile/packages/tx/ledger/plutus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,31 @@ export const createLedgerPlutusPayload = async (

const originalRequiredSigners = getRequiredSigners(body)

const requiredSigners = originalRequiredSigners.map((s) => {
const paymentStakeCredential = csl.Credential.fromKeyhash(s)
const stakeCredential = csl.Credential.fromKeyhash(stakeVKHash)
const baseAddress = csl.BaseAddress.new(
networkId,
paymentStakeCredential,
stakeCredential,
)
const addressing = getAddressAddressing(
baseAddress.toAddress().toBech32(undefined),
)
if (!addressing)
throw new Error(
`Could not find addressing for required signer: ${s.toHex()}`,
// Only include required signers that the wallet actually controls
// Skip signers we don't control (e.g., Minswap's payment key + our staking key)
const requiredSigners = originalRequiredSigners
.map((s) => {
const paymentStakeCredential = csl.Credential.fromKeyhash(s)
const stakeCredential = csl.Credential.fromKeyhash(stakeVKHash)
const baseAddress = csl.BaseAddress.new(
networkId,
paymentStakeCredential,
stakeCredential,
)
const path = addressing.path
return {type: TxRequiredSignerType.PATH as const, path}
})
const addressing = getAddressAddressing(
baseAddress.toAddress().toBech32(undefined),
)
if (!addressing) {
// Skip if wallet doesn't control this address
return null
}
const path = addressing.path
return {type: TxRequiredSignerType.PATH as const, path}
})
.filter(
(s): s is {type: TxRequiredSignerType.PATH; path: number[]} =>
s !== null,
)

const inputs = body.inputs()
const inputsArray: TxInput[] = []
Expand Down
13 changes: 12 additions & 1 deletion mobile/packages/tx/ledger/signers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,21 @@ const getRequiredSignersAddressing = async ({
const addressingArray: Addressing[] = []

for (const signer of signersArray) {
if (stakingKeyPath) {
// Check if this required signer matches the wallet's staking key hash
const signerKeyHashHex = signer.toHex()
const walletStakingKeyHashHex = stakeVKHash.toHex()
const isStakingKeySigner = signerKeyHashHex === walletStakingKeyHashHex

if (stakingKeyPath && isStakingKeySigner) {
// Only add staking key signer if the required signer actually matches our staking key
addressingArray.push({
path: stakingKeyPath,
startLevel: 1,
})
continue
}

// For payment key signers, construct the address and check if wallet controls it
const paymentStakeCredential = wasm.Credential.fromKeyhash(signer)
const stakeCredential = wasm.Credential.fromKeyhash(stakeVKHash)
const baseAddress = wasm.BaseAddress.new(
Expand All @@ -175,14 +182,18 @@ const getRequiredSignersAddressing = async ({
)
const bech32Address = baseAddress.toAddress().toBech32(undefined)
const addressing = getAddressAddressing(bech32Address)

// Only include if we can get addressing AND the address is actually controlled by the wallet
if (!addressing) {
if (!partial) {
throw new Error(
`Could not find addressing for required signer: ${signer.toHex()}`,
)
}
// Skip if we don't control this address (partial mode)
continue
}

addressingArray.push(addressing)
}

Expand Down
Loading