diff --git a/packages/core/src/Cardano/util/index.ts b/packages/core/src/Cardano/util/index.ts index fc740d36280..3ce211e98d4 100644 --- a/packages/core/src/Cardano/util/index.ts +++ b/packages/core/src/Cardano/util/index.ts @@ -5,3 +5,4 @@ export * from './resolveInputValue'; export * from './phase2Validation'; export * from './addressesShareAnyKey'; export * from './plutusDataUtils'; +export * from './isScriptAddress'; diff --git a/packages/core/src/Cardano/util/isScriptAddress.ts b/packages/core/src/Cardano/util/isScriptAddress.ts new file mode 100644 index 00000000000..bda9daaac8f --- /dev/null +++ b/packages/core/src/Cardano/util/isScriptAddress.ts @@ -0,0 +1,8 @@ +import { Address, CredentialType, PaymentAddress } from '../Address'; + +export const isScriptAddress = (address: PaymentAddress): boolean => { + const baseAddress = Address.fromBech32(address).asBase(); + const paymentCredential = baseAddress?.getPaymentCredential(); + const stakeCredential = baseAddress?.getStakeCredential(); + return paymentCredential?.type === CredentialType.ScriptHash && stakeCredential?.type === CredentialType.ScriptHash; +}; diff --git a/packages/core/test/Cardano/util/isScriptAddress.test.ts b/packages/core/test/Cardano/util/isScriptAddress.test.ts new file mode 100644 index 00000000000..33c1dd2ae96 --- /dev/null +++ b/packages/core/test/Cardano/util/isScriptAddress.test.ts @@ -0,0 +1,18 @@ +import { PaymentAddress } from '../../../src/Cardano'; +import { isScriptAddress } from '../../../src/Cardano/util'; + +describe('isScriptAddress', () => { + it('returns false when it receives a non-script address', () => { + const nonScriptAddress = PaymentAddress( + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' + ); + expect(isScriptAddress(nonScriptAddress)).toBe(false); + }); + + it('returns true when it receives a script address', () => { + const scriptAddress = PaymentAddress( + 'addr_test1xr806j8xcq6cw6jjkzfxyewyue33zwnu4ajnu28hakp5fmc6gddlgeqee97vwdeafwrdgrtzp2rw8rlchjf25ld7r2ssptq3m9' + ); + expect(isScriptAddress(scriptAddress)).toBe(true); + }); +}); diff --git a/packages/hardware-ledger/src/LedgerKeyAgent.ts b/packages/hardware-ledger/src/LedgerKeyAgent.ts index 5bfb7ec3cc1..12c792f9efc 100644 --- a/packages/hardware-ledger/src/LedgerKeyAgent.ts +++ b/packages/hardware-ledger/src/LedgerKeyAgent.ts @@ -322,6 +322,10 @@ const getDerivationPath = ( }; }; +const multiSigWitnessPaths: BIP32Path[] = [ + util.accountKeyDerivationPathToBip32Path(0, { index: 0, role: KeyRole.External }, KeyPurpose.MULTI_SIG) +]; + export class LedgerKeyAgent extends KeyAgentBase { readonly deviceConnection?: LedgerConnection; readonly #communicationType: CommunicationType; @@ -733,7 +737,10 @@ export class LedgerKeyAgent extends KeyAgentBase { tagCborSets: txBody.hasTaggedSets() }, signingMode, - tx: ledgerTxData + tx: ledgerTxData, + ...(signingMode === TransactionSigningMode.MULTISIG_TRANSACTION && { + additionalWitnessPaths: multiSigWitnessPaths + }) }); if (!areStringsEqualInConstantTime(result.txHashHex, hash)) { diff --git a/packages/hardware-ledger/src/transformers/txOut.ts b/packages/hardware-ledger/src/transformers/txOut.ts index a99e53f07cf..d59120bb27e 100644 --- a/packages/hardware-ledger/src/transformers/txOut.ts +++ b/packages/hardware-ledger/src/transformers/txOut.ts @@ -20,8 +20,9 @@ const toDestination: Transform { const knownAddress = context?.knownAddresses.find((address) => address.address === txOut.address); + const isScriptAddress = Cardano.util.isScriptAddress(txOut.address); - if (knownAddress) { + if (knownAddress && !isScriptAddress) { const paymentKeyPath = util.paymentKeyPathFromGroupedAddress(knownAddress); const stakeKeyPath = util.stakeKeyPathFromGroupedAddress(knownAddress); diff --git a/packages/hardware-trezor/src/TrezorKeyAgent.ts b/packages/hardware-trezor/src/TrezorKeyAgent.ts index 20ad6e01656..f48e7c80c70 100644 --- a/packages/hardware-trezor/src/TrezorKeyAgent.ts +++ b/packages/hardware-trezor/src/TrezorKeyAgent.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as Crypto from '@cardano-sdk/crypto'; import * as Trezor from '@trezor/connect'; +import { BIP32Path } from '@cardano-sdk/crypto'; import { Cardano, NotImplementedError, Serialization } from '@cardano-sdk/core'; import { CardanoKeyConst, @@ -9,6 +10,7 @@ import { KeyAgentDependencies, KeyAgentType, KeyPurpose, + KeyRole, SerializableTrezorKeyAgentData, SignBlobResult, SignTransactionContext, @@ -68,6 +70,10 @@ const containsOnlyScriptHashCredentials = (tx: Omit !withdrawal.scriptHash); }; +const multiSigWitnessPaths: BIP32Path[] = [ + util.accountKeyDerivationPathToBip32Path(0, { index: 0, role: KeyRole.External }, KeyPurpose.MULTI_SIG) +]; + const isMultiSig = (tx: Omit): boolean => { const allThirdPartyInputs = !tx.inputs.some((input) => input.path); // Trezor doesn't allow change outputs to address controlled by your keys and instead you have to use script address for change out @@ -100,7 +106,8 @@ export class TrezorKeyAgent extends KeyAgentBase { manifest, communicationType, silentMode = false, - lazyLoad = false + lazyLoad = false, + shouldHandlePassphrase = false }: TrezorConfig): Promise { const trezorConnect = getTrezorConnect(communicationType); try { @@ -116,6 +123,23 @@ export class TrezorKeyAgent extends KeyAgentBase { // Show Trezor Suite popup. Disabled for node based apps popup: communicationType !== CommunicationType.Node && !silentMode }); + + if (shouldHandlePassphrase) { + trezorConnect.on(Trezor.UI_EVENT, (event) => { + // React on ui-request_passphrase event + if (event.type === Trezor.UI.REQUEST_PASSPHRASE && event.payload.device) { + trezorConnect.uiResponse({ + payload: { + passphraseOnDevice: true, + save: true, + value: '' + }, + type: Trezor.UI.RECEIVE_PASSPHRASE + }); + } + }); + } + return true; } catch (error: any) { if (error.code === 'Init_AlreadyInitialized') return true; @@ -215,7 +239,7 @@ export class TrezorKeyAgent extends KeyAgentBase { async signTransaction( txBody: Serialization.TransactionBody, - { knownAddresses, txInKeyPathMap }: SignTransactionContext + { knownAddresses, txInKeyPathMap, scripts }: SignTransactionContext ): Promise { try { await this.isTrezorInitialized; @@ -235,12 +259,15 @@ export class TrezorKeyAgent extends KeyAgentBase { const trezorConnect = getTrezorConnect(this.#communicationType); const result = await trezorConnect.cardanoSignTransaction({ ...trezorTxData, + ...(signingMode === Trezor.PROTO.CardanoTxSigningMode.MULTISIG_TRANSACTION && { + additionalWitnessRequests: multiSigWitnessPaths + }), signingMode }); const expectedPublicKeys = await Promise.all( util - .ownSignatureKeyPaths(body, knownAddresses, txInKeyPathMap) + .ownSignatureKeyPaths(body, knownAddresses, txInKeyPathMap, undefined, scripts) .map((derivationPath) => this.derivePublicKey(derivationPath)) ); diff --git a/packages/hardware-trezor/src/transformers/txOut.ts b/packages/hardware-trezor/src/transformers/txOut.ts index 38524a222ec..65359b3b50d 100644 --- a/packages/hardware-trezor/src/transformers/txOut.ts +++ b/packages/hardware-trezor/src/transformers/txOut.ts @@ -10,8 +10,9 @@ const toDestination: Transform { const knownAddress = context?.knownAddresses.find((address: GroupedAddress) => address.address === txOut.address); + const isScriptAddress = Cardano.util.isScriptAddress(txOut.address); - if (!knownAddress) { + if (!knownAddress || isScriptAddress) { return { address: txOut.address }; diff --git a/packages/key-management/src/types.ts b/packages/key-management/src/types.ts index 720df46c314..29200902d84 100644 --- a/packages/key-management/src/types.ts +++ b/packages/key-management/src/types.ts @@ -92,6 +92,8 @@ export interface TrezorConfig { email: string; appUrl: string; }; + /** When set to true, Trezor automatically handle passphrase entry by forcing it to occur on the device */ + shouldHandlePassphrase?: boolean; } export interface SerializableKeyAgentDataBase { diff --git a/packages/key-management/src/util/ownSignatureKeyPaths.ts b/packages/key-management/src/util/ownSignatureKeyPaths.ts index 18c9c1806eb..dc560f908c6 100644 --- a/packages/key-management/src/util/ownSignatureKeyPaths.ts +++ b/packages/key-management/src/util/ownSignatureKeyPaths.ts @@ -1,5 +1,5 @@ import * as Crypto from '@cardano-sdk/crypto'; -import { AccountKeyDerivationPath, GroupedAddress, TxInId, TxInKeyPathMap } from '../types'; +import { AccountKeyDerivationPath, GroupedAddress, KeyRole, TxInId, TxInKeyPathMap } from '../types'; import { Cardano } from '@cardano-sdk/core'; import { DREP_KEY_DERIVATION_PATH } from './key'; import { Ed25519KeyHashHex } from '@cardano-sdk/crypto'; @@ -300,15 +300,24 @@ const checkStakeCredential = (address: GroupedAddress, keyHash: Crypto.Ed25519Ke ? { derivationPaths: [address.stakeKeyDerivationPath], requiresForeignSignatures: false } : { derivationPaths: [], requiresForeignSignatures: true }; -const checkPaymentCredential = (address: GroupedAddress, keyHash: Crypto.Ed25519KeyHashHex): SignatureCheck => { +const checkPaymentCredential = (address: GroupedAddress, keyHash: Crypto.Ed25519KeyHashHex) => { const paymentCredential = Cardano.Address.fromBech32(address.address)?.asBase()?.getPaymentCredential(); - return paymentCredential?.type === Cardano.CredentialType.KeyHash && + if ( + paymentCredential?.type === Cardano.CredentialType.ScriptHash && paymentCredential.hash === Crypto.Hash28ByteBase16.fromEd25519KeyHashHex(keyHash) - ? { - derivationPaths: [{ index: address.index, role: Number(address.type) }], - requiresForeignSignatures: false - } - : { derivationPaths: [], requiresForeignSignatures: true }; + ) + return { + derivationPaths: [{ index: address.index, role: Number(address.type) }], + requiresForeignSignatures: false + }; + + if (paymentCredential?.type === Cardano.CredentialType.ScriptHash) { + return { + derivationPaths: [{ index: address.index, role: KeyRole.External }], + requiresForeignSignatures: false + }; + } + return { derivationPaths: [], requiresForeignSignatures: true }; }; const combineSignatureChecks = (a: SignatureCheck, b: SignatureCheck): SignatureCheck => ({ diff --git a/packages/key-management/test/util/ownSignaturePaths.test.ts b/packages/key-management/test/util/ownSignaturePaths.test.ts index 41d3ad04fc7..760c037da8f 100644 --- a/packages/key-management/test/util/ownSignaturePaths.test.ts +++ b/packages/key-management/test/util/ownSignaturePaths.test.ts @@ -33,7 +33,7 @@ const createGroupedAddress = ( rewardAccount: Cardano.RewardAccount, type: AddressType, index: number, - stakeKeyDerivationPath: AccountKeyDerivationPath + stakeKeyDerivationPath?: AccountKeyDerivationPath ) => ({ ...createBaseGroupedAddress(address, rewardAccount, type, index), @@ -633,5 +633,45 @@ describe('KeyManagement.util.ownSignaturePaths', () => { expect(util.ownSignatureKeyPaths(txBody, [knownAddress1], {}, undefined, scripts)).toEqual([]); }); + it('includes derivation paths for multi-signature native scripts', async () => { + const scriptAddress = Cardano.PaymentAddress( + 'addr_test1xr806j8xcq6cw6jjkzfxyewyue33zwnu4ajnu28hakp5fmc6gddlgeqee97vwdeafwrdgrtzp2rw8rlchjf25ld7r2ssptq3m9' + ); + const scriptRewardAccount = Cardano.RewardAccount( + 'stake_test17qdyxkl5vsvujlx8xu75hpk5p43q4phr3lutey420klp4gg7zmhrn' + ); + const txBody: Cardano.TxBody = { + fee: BigInt(0), + inputs: [{}, {}, {}] as Cardano.TxIn[], + outputs: [] + }; + + const scripts: Cardano.Script[] = [ + { + __type: Cardano.ScriptType.Native, + kind: Cardano.NativeScriptKind.RequireAnyOf, + scripts: [ + { + __type: Cardano.ScriptType.Native, + keyHash: Ed25519KeyHashHex('b498c0eaceb9a8c7c829d36fc84e892113c9d2636b53b0636d7518b4'), + kind: Cardano.NativeScriptKind.RequireSignature + }, + { + __type: Cardano.ScriptType.Native, + keyHash: Ed25519KeyHashHex(otherStakeKeyHash), + kind: Cardano.NativeScriptKind.RequireSignature + } + ] + } + ]; + + const knownAddress = createGroupedAddress(scriptAddress, scriptRewardAccount, AddressType.External, 0); + expect(util.ownSignatureKeyPaths(txBody, [knownAddress], {}, undefined, scripts)).toEqual([ + { + index: 0, + role: KeyRole.External + } + ]); + }); }); }); diff --git a/packages/wallet/test/hardware/ledger/LedgerSharedWalletKeyAgent.test.ts b/packages/wallet/test/hardware/ledger/LedgerSharedWalletKeyAgent.test.ts new file mode 100644 index 00000000000..ea51979598e --- /dev/null +++ b/packages/wallet/test/hardware/ledger/LedgerSharedWalletKeyAgent.test.ts @@ -0,0 +1,112 @@ +import * as Crypto from '@cardano-sdk/crypto'; +import { BaseWallet, createSharedWallet } from '../../../src'; +import { Cardano } from '@cardano-sdk/core'; +import { CommunicationType, KeyPurpose, KeyRole, util } from '@cardano-sdk/key-management'; +import { InitializeTxProps, InitializeTxResult } from '@cardano-sdk/tx-construction'; +import { LedgerKeyAgent } from '@cardano-sdk/hardware-ledger'; +import { dummyLogger as logger } from 'ts-log'; +import { mockProviders as mocks } from '@cardano-sdk/util-dev'; + +describe('LedgerSharedWalletKeyAgent', () => { + let ledgerKeyAgent: LedgerKeyAgent; + let wallet: BaseWallet; + + beforeAll(async () => { + ledgerKeyAgent = await LedgerKeyAgent.createWithDevice( + { + chainId: Cardano.ChainIds.Preprod, + communicationType: CommunicationType.Node, + purpose: KeyPurpose.MULTI_SIG + }, + { bip32Ed25519: await Crypto.SodiumBip32Ed25519.create(), logger } + ); + }); + + afterAll(async () => { + await ledgerKeyAgent.deviceConnection?.transport.close(); + }); + + describe('signTransaction', () => { + let txInternals: InitializeTxResult; + + beforeAll(async () => { + const walletPubKey = await ledgerKeyAgent.derivePublicKey({ index: 0, role: KeyRole.External }); + const walletKeyHash = ledgerKeyAgent.bip32Ed25519.getPubKeyHash(walletPubKey); + + const walletStakePubKey = await ledgerKeyAgent.derivePublicKey({ index: 0, role: KeyRole.Stake }); + const walletStakeKeyHash = ledgerKeyAgent.bip32Ed25519.getPubKeyHash(walletStakePubKey); + + const paymentScript: Cardano.NativeScript = { + __type: Cardano.ScriptType.Native, + kind: Cardano.NativeScriptKind.RequireAnyOf, + scripts: [ + { + __type: Cardano.ScriptType.Native, + keyHash: walletKeyHash, + kind: Cardano.NativeScriptKind.RequireSignature + }, + { + __type: Cardano.ScriptType.Native, + keyHash: Crypto.Ed25519KeyHashHex('b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398539'), + kind: Cardano.NativeScriptKind.RequireSignature + } + ] + }; + + const stakingScript: Cardano.NativeScript = { + __type: Cardano.ScriptType.Native, + kind: Cardano.NativeScriptKind.RequireAnyOf, + scripts: [ + { + __type: Cardano.ScriptType.Native, + keyHash: walletStakeKeyHash, + kind: Cardano.NativeScriptKind.RequireSignature + }, + { + __type: Cardano.ScriptType.Native, + keyHash: Crypto.Ed25519KeyHashHex('b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398539'), + kind: Cardano.NativeScriptKind.RequireSignature + } + ] + }; + + const outputs: Cardano.TxOut[] = [ + { + address: Cardano.PaymentAddress( + 'addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w' + ), + scriptReference: paymentScript, + value: { coins: 11_111_111n } + } + ]; + const props: InitializeTxProps = { + outputs: new Set(outputs) + }; + + wallet = createSharedWallet( + { name: 'Shared HW Wallet' }, + { + assetProvider: mocks.mockAssetProvider(), + chainHistoryProvider: mocks.mockChainHistoryProvider(), + logger, + networkInfoProvider: mocks.mockNetworkInfoProvider(), + paymentScript, + rewardAccountInfoProvider: mocks.mockRewardAccountInfoProvider(), + rewardsProvider: mocks.mockRewardsProvider(), + stakingScript, + txSubmitProvider: mocks.mockTxSubmitProvider(), + utxoProvider: mocks.mockUtxoProvider(), + witnesser: util.createBip32Ed25519Witnesser(util.createAsyncKeyAgent(ledgerKeyAgent)) + } + ); + txInternals = await wallet.initializeTx(props); + }); + + afterAll(() => wallet.shutdown()); + + it('successfully signs a transaction', async () => { + const tx = await wallet.finalizeTx({ tx: txInternals }); + expect(tx.witness.signatures.size).toBe(1); + }); + }); +}); diff --git a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts index bfc523b5bc1..efa93f6d97e 100644 --- a/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts +++ b/packages/wallet/test/hardware/trezor/TrezorKeyAgent.test.ts @@ -4,6 +4,7 @@ import { Bip32Account, CommunicationType, SerializableTrezorKeyAgentData, + TrezorConfig, util } from '@cardano-sdk/key-management'; import { AssetId, mockProviders as mocks } from '@cardano-sdk/util-dev'; @@ -31,12 +32,13 @@ describe('TrezorKeyAgent', () => { let txSubmitProvider: mocks.TxSubmitProviderStub; let address: Cardano.PaymentAddress; - const trezorConfig = { + const trezorConfig: TrezorConfig = { communicationType: CommunicationType.Node, manifest: { appUrl: 'https://your.application.com', email: 'email@developer.com' - } + }, + shouldHandlePassphrase: true }; beforeAll(async () => { diff --git a/packages/wallet/test/hardware/trezor/TrezorSharedWalletKeyAgent.test.ts b/packages/wallet/test/hardware/trezor/TrezorSharedWalletKeyAgent.test.ts new file mode 100644 index 00000000000..f4a300cf30a --- /dev/null +++ b/packages/wallet/test/hardware/trezor/TrezorSharedWalletKeyAgent.test.ts @@ -0,0 +1,120 @@ +import * as Crypto from '@cardano-sdk/crypto'; +import { BaseWallet, createSharedWallet } from '../../../src'; +import { Cardano } from '@cardano-sdk/core'; +import { CommunicationType, KeyPurpose, KeyRole, TrezorConfig, util } from '@cardano-sdk/key-management'; +import { InitializeTxProps, InitializeTxResult } from '@cardano-sdk/tx-construction'; +import { TrezorKeyAgent } from '@cardano-sdk/hardware-trezor'; +import { dummyLogger as logger } from 'ts-log'; +import { mockProviders as mocks } from '@cardano-sdk/util-dev'; + +describe('TrezorSharedWalletKeyAgent', () => { + let wallet: BaseWallet; + let trezorKeyAgent: TrezorKeyAgent; + let paymentScript: Cardano.NativeScript; + + const trezorConfig: TrezorConfig = { + communicationType: CommunicationType.Node, + manifest: { + appUrl: 'https://your.application.com', + email: 'email@developer.com' + }, + shouldHandlePassphrase: true + }; + + beforeAll(async () => { + trezorKeyAgent = await TrezorKeyAgent.createWithDevice( + { + chainId: Cardano.ChainIds.Preprod, + purpose: KeyPurpose.MULTI_SIG, + trezorConfig + }, + { + bip32Ed25519: await Crypto.SodiumBip32Ed25519.create(), + logger + } + ); + + const paymentKeyHash = trezorKeyAgent.bip32Ed25519.getPubKeyHash( + await trezorKeyAgent.derivePublicKey({ index: 0, role: KeyRole.External }) + ); + const stakeKeyHash = trezorKeyAgent.bip32Ed25519.getPubKeyHash( + await trezorKeyAgent.derivePublicKey({ index: 0, role: KeyRole.Stake }) + ); + + paymentScript = { + __type: Cardano.ScriptType.Native, + kind: Cardano.NativeScriptKind.RequireAnyOf, + scripts: [ + { + __type: Cardano.ScriptType.Native, + keyHash: paymentKeyHash, + kind: Cardano.NativeScriptKind.RequireSignature + }, + { + __type: Cardano.ScriptType.Native, + keyHash: Crypto.Ed25519KeyHashHex('b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398539'), + kind: Cardano.NativeScriptKind.RequireSignature + } + ] + }; + + const stakingScript: Cardano.NativeScript = { + __type: Cardano.ScriptType.Native, + kind: Cardano.NativeScriptKind.RequireAnyOf, + scripts: [ + { + __type: Cardano.ScriptType.Native, + keyHash: stakeKeyHash, + kind: Cardano.NativeScriptKind.RequireSignature + }, + { + __type: Cardano.ScriptType.Native, + keyHash: Crypto.Ed25519KeyHashHex('b275b08c999097247f7c17e77007c7010cd19f20cc086ad99d398539'), + kind: Cardano.NativeScriptKind.RequireSignature + } + ] + }; + + wallet = createSharedWallet( + { name: 'Shared HW Wallet' }, + { + assetProvider: mocks.mockAssetProvider(), + chainHistoryProvider: mocks.mockChainHistoryProvider(), + logger, + networkInfoProvider: mocks.mockNetworkInfoProvider(), + paymentScript, + rewardAccountInfoProvider: mocks.mockRewardAccountInfoProvider(), + rewardsProvider: mocks.mockRewardsProvider(), + stakingScript, + txSubmitProvider: mocks.mockTxSubmitProvider(), + utxoProvider: mocks.mockUtxoProvider(), + witnesser: util.createBip32Ed25519Witnesser(util.createAsyncKeyAgent(trezorKeyAgent)) + } + ); + }); + + afterAll(() => wallet.shutdown()); + + describe('Sign Transaction', () => { + let props: InitializeTxProps; + let txInternals: InitializeTxResult; + const simpleOutput = { + address: Cardano.PaymentAddress( + 'addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w' + ), + value: { coins: 11_111_111n } + }; + + it('should sign simple multi-sig transaction', async () => { + props = { + outputs: new Set([simpleOutput]) + }; + txInternals = await wallet.initializeTx(props); + const witnessedTx = await wallet.finalizeTx({ + signingContext: { scripts: [paymentScript] }, + tx: txInternals + }); + expect(witnessedTx.witness.signatures.size).toBe(1); + }); + }); +}); diff --git a/yarn-project.nix b/yarn-project.nix index 7b93fa06ec4..aa17c2bffdd 100644 --- a/yarn-project.nix +++ b/yarn-project.nix @@ -441,7 +441,7 @@ cacheEntries = { "@emurgo/cip14-js@npm:3.0.1" = { filename = "@emurgo-cip14-js-npm-3.0.1-6011030ea2-9eaf312410.zip"; sha512 = "9eaf3124108e8c252a745de9ef1f334ab26a32271077b00fe0ea2a06e40838dd435165dac523ebd4d851ae7a94d8c56766dabc372aabffedd36551c798c607c5"; }; "@endemolshinegroup/cosmiconfig-typescript-loader@npm:3.0.2" = { filename = "@endemolshinegroup-cosmiconfig-typescript-loader-npm-3.0.2-97436e68fc-7fe0198622.zip"; sha512 = "7fe0198622b1063c40572034df7e8ba867865a1b4815afe230795929abcf785758b34d7806a8e2100ba8ab4e92c5a1c3e11a980c466c4406df6e7ec6e50df8b6"; }; "@es-joy/jsdoccomment@npm:0.10.8" = { filename = "@es-joy-jsdoccomment-npm-0.10.8-d03c65b162-3e144ef393.zip"; sha512 = "3e144ef393459a541b64f6c9c8e62fb6d9b47e1a2c626410487ede12c472064f6ce6e0911df60b42ccf126d5a66102707eef59ca14767cb7aeb5e608b227558d"; }; -"@esbuild/linux-x64@npm:0.21.5" = { filename = "@esbuild-linux-x64-npm-0.21.5-88079726c4-8.zip"; sha512 = "91c202dca064909b2c56522f98e3a3b24bc5d43405506b4e67923ecb5d0cc2b78dcee8d815f705d71395402f8532670a391777a3cf6a08894049e453becf07a0"; }; +"@esbuild/darwin-arm64@npm:0.21.5" = { filename = "@esbuild-darwin-arm64-npm-0.21.5-62349c1520-8.zip"; sha512 = "50d5d633be3d0fe0fce54c4740171ae6d2e8f5220280a6f6996f234c718de25535e50a31cee1745b5b80f2cc9e336c42c7fc2b49f3ea38b5f3ff5d8c97ef4123"; }; "@eslint/eslintrc@npm:0.4.3" = { filename = "@eslint-eslintrc-npm-0.4.3-ee1bbcab87-03a7704150.zip"; sha512 = "03a7704150b868c318aab6a94d87a33d30dc2ec579d27374575014f06237ba1370ae11178db772f985ef680d469dc237e7b16a1c5d8edaaeb8c3733e7a95a6d3"; }; "@ethereumjs/common@npm:4.4.0" = { filename = "@ethereumjs-common-npm-4.4.0-ee991f5124-6b8cbfcfb5.zip"; sha512 = "6b8cbfcfb5bdde839545c89dce3665706733260e26455d0eb3bcbc3c09e371ae629d51032b95d86f2aeeb15325244a6622171f9005165266fefd923eaa99f1c5"; }; "@ethereumjs/rlp@npm:5.0.2" = { filename = "@ethereumjs-rlp-npm-5.0.2-72fb389b37-b569061ddb.zip"; sha512 = "b569061ddb1f4cf56a82f7a677c735ba37f9e94e2bbaf567404beb9e2da7aa1f595e72fc12a17c61f7aec67fd5448443efe542967c685a2fe0ffc435793dcbab"; }; @@ -1461,6 +1461,8 @@ cacheEntries = { "fs.realpath@npm:1.0.0" = { filename = "fs.realpath-npm-1.0.0-c8f05d8126-99ddea01a7.zip"; sha512 = "99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0"; }; "fsevents@npm:2.3.2" = { filename = "fsevents-npm-2.3.2-a881d6ac9f-97ade64e75.zip"; sha512 = "97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f"; }; "fsevents@npm:2.3.3" = { filename = "fsevents-npm-2.3.3-ce9fb0ffae-11e6ea6fea.zip"; sha512 = "11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317"; }; +"fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" = { filename = "fsevents-patch-3340e2eb10-8.zip"; sha512 = "edbd0fd80be379c14409605f77e52fdc78a119e17f875e8b90a220c3e5b29e54a1477c21d91fd30b957ea4866406dc3ff87b61432d2840ff8866b309e5866140"; }; +"fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=18f3a7" = { filename = "fsevents-patch-7934e3c202-8.zip"; sha512 = "4639e24e2774cbd3669bd08521e0eeeb9d05bbabffdfdee418cc75a237660bc2fb30520a266ad5379199e2d657f430dd4236ad3642674ef32f20cc7258506725"; }; "ftp@npm:0.3.10" = { filename = "ftp-npm-0.3.10-348fb9ac23-ddd313c1d4.zip"; sha512 = "ddd313c1d44eb7429f3a7d77a0155dc8fe86a4c64dca58f395632333ce4b4e74c61413c6e0ef66ea3f3d32d905952fbb6d028c7117d522f793eb1fa282e17357"; }; "function-bind@npm:1.1.1" = { filename = "function-bind-npm-1.1.1-b56b322ae9-b32fbaebb3.zip"; sha512 = "b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a"; }; "function.prototype.name@npm:1.1.5" = { filename = "function.prototype.name-npm-1.1.5-e776a642bb-acd21d733a.zip"; sha512 = "acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27"; };