From 3f63cfdee85705572a55a52e59661351ae1c064c Mon Sep 17 00:00:00 2001 From: Ashwin van Dijk Date: Mon, 7 Aug 2023 13:16:37 +0200 Subject: [PATCH 1/6] Adds signWithEckoWallet and quicksignWithEckoWallet functions --- packages/libs/client/etc/client.api.md | 10 + .../client/src/interfaces/ISigningRequest.ts | 16 ++ .../src/signing/eckoWallet/eckoCommon.ts | 60 ++++++ .../src/signing/eckoWallet/eckoTypes.ts | 51 +++++ .../eckoWallet/quicksignWithEckoWallet.ts | 100 +++++++++ .../signing/eckoWallet/signWithEckoWallet.ts | 63 ++++++ .../tests/quicksignWithEckoWallet.test.ts | 195 ++++++++++++++++++ .../tests/signWithEckoWallet.test.ts | 169 +++++++++++++++ packages/libs/client/src/signing/index.ts | 5 +- .../utils/pactCommandToSigningRequest.ts | 39 ++++ .../walletconnect/signWithWalletConnect.ts | 3 +- .../walletconnect/walletConnectTypes.ts | 20 -- 12 files changed, 709 insertions(+), 22 deletions(-) create mode 100644 packages/libs/client/src/interfaces/ISigningRequest.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/eckoCommon.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/eckoTypes.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts create mode 100644 packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts create mode 100644 packages/libs/client/src/signing/utils/pactCommandToSigningRequest.ts diff --git a/packages/libs/client/etc/client.api.md b/packages/libs/client/etc/client.api.md index 009e1ba157..a38df96b4e 100644 --- a/packages/libs/client/etc/client.api.md +++ b/packages/libs/client/etc/client.api.md @@ -30,6 +30,16 @@ export { ChainId } // @public export const createClient: ICreateClient; +// Warning: (ae-forgotten-export) The symbol "IEckoSignFunction" needs to be exported by the entry point index.d.ts +// +// @public +export function createEckoWalletQuicksign(): IEckoSignFunction; + +// Warning: (ae-forgotten-export) The symbol "IEckoSignSingleFunction" needs to be exported by the entry point index.d.ts +// +// @public +export function createEckoWalletSign(): IEckoSignSingleFunction; + // @public export const createTransaction: (pactCommand: Partial) => IUnsignedCommand; diff --git a/packages/libs/client/src/interfaces/ISigningRequest.ts b/packages/libs/client/src/interfaces/ISigningRequest.ts new file mode 100644 index 0000000000..aca4600ecf --- /dev/null +++ b/packages/libs/client/src/interfaces/ISigningRequest.ts @@ -0,0 +1,16 @@ +import { ISigningCap } from '@kadena/types'; + +import { IPactCommand } from './IPactCommand'; + +export interface ISigningRequest { + code: string; + data?: Record; + caps: ISigningCap[]; + nonce?: string; + chainId?: IPactCommand['meta']['chainId']; + gasLimit?: number; + gasPrice?: number; + ttl?: number; + sender?: string; + extraSigners?: string[]; +} diff --git a/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts new file mode 100644 index 0000000000..12219a0d61 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts @@ -0,0 +1,60 @@ +import { + IEckoAccountsResponse, + IEckoConnectOrStatusResponse, +} from './eckoTypes'; + +export const isInstalled: ICommonEckoFunctions['isInstalled'] = () => { + const { kadena } = window; + return Boolean(kadena && kadena.isKadena); +}; + +export const isConnected: ICommonEckoFunctions['isConnected'] = async ( + networkId, +) => { + if (!isInstalled()) { + return false; + } + + const checkStatusResponse = + await window.kadena?.request({ + method: 'kda_checkStatus', + networkId, + }); + + if (checkStatusResponse?.status === 'fail') { + return false; + } + + return true; +}; + +export const connect: ICommonEckoFunctions['connect'] = async (networkId) => { + if (!isInstalled()) { + throw new Error('Ecko Wallet is not installed'); + } + + if (await isConnected(networkId)) { + return true; + } + + const connectResponse = + await window.kadena?.request({ + method: 'kda_connect', + networkId, + }); + + if (connectResponse?.status === 'fail') { + throw new Error('User declined connection'); + } + + return true; +}; + +export const getAccountInfo = async ( + networkId: string, +): Promise => { + return window.kadena?.request({ + method: 'kda_requestAccount', + networkId, + }); +}; diff --git a/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts new file mode 100644 index 0000000000..a6bd824ce1 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts @@ -0,0 +1,51 @@ +import { ICommand } from '@kadena/types'; + +import { IQuicksignResponseOutcomes } from '../../signing-api/v1/quicksign'; + +export type EckoStatus = 'success' | 'fail'; + +export interface ICommonEckoFunctions { + isInstalled: () => boolean; + isConnected: (networkId: string) => Promise; + connect: (networkId: string) => Promise; +} + +export interface IEckoConnectOrStatusResponse { + status: EckoStatus; + message?: string; + account?: { + account: string; + publicKey: string; + connectedSites: string[]; + }; +} + +export interface IEckoSignResponse { + status: EckoStatus; + signedCmd: ICommand; +} + +export interface IEckoQuicksignSuccessResponse { + status: 'success'; + quickSignData: IQuicksignResponseOutcomes['responses']; +} + +export interface IEckoQuicksignFailResponse { + status: 'fail'; + error: string; +} + +export type IEckoQuicksignResponse = + | IEckoQuicksignSuccessResponse + | IEckoQuicksignFailResponse; + +export interface IEckoAccountsResponse { + status: EckoStatus; + message?: string; + wallet?: { + account: string; + publicKey: string; + connectedSites: string[]; + balance: number; + }; +} diff --git a/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts b/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts new file mode 100644 index 0000000000..7eb3c8d7f6 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts @@ -0,0 +1,100 @@ +import { ICommand, IUnsignedCommand } from '@kadena/types'; + +import { ISignFunction } from '../ISignFunction'; +import { addSignatures } from '../utils/addSignature'; +import { parseTransactionCommand } from '../utils/parseTransactionCommand'; + +import { + connect, + getAccountInfo, + isConnected, + isInstalled, +} from './eckoCommon'; +import { ICommonEckoFunctions, IEckoQuicksignResponse } from './eckoTypes'; + +interface IEckoSignFunction extends ISignFunction, ICommonEckoFunctions {} + +/** + * Creates the quicksignWithWalletConnect function with interface {@link ISingleSignFunction} + * + * @public + */ +export function createEckoWalletQuicksign(): IEckoSignFunction { + const quicksignWithEckoWallet: IEckoSignFunction = (async ( + transactionList: IUnsignedCommand | Array, + ) => { + if (transactionList === undefined) { + throw new Error('No transaction(s) to sign'); + } + const isList = Array.isArray(transactionList); + const transactions = isList ? transactionList : [transactionList]; + + const transactionHashes: string[] = []; + + const { networkId } = parseTransactionCommand(transactions[0]); + + const commandSigDatas = transactions.map((pactCommand) => { + const { cmd, hash } = pactCommand; + const parsedTransaction = parseTransactionCommand(pactCommand); + transactionHashes.push(hash); + + if (networkId !== parsedTransaction.networkId) { + throw new Error('Network is not equal for all transactions'); + } + + return { + cmd, + sigs: parsedTransaction.signers.map((signer, i) => ({ + pubKey: signer.pubKey, + sig: pactCommand.sigs[i]?.sig ?? null, + })), + }; + }); + + const eckoResponse = await window.kadena?.request({ + method: 'kda_requestQuickSign', + data: { + networkId, + commandSigDatas, + }, + }); + + if (!eckoResponse || eckoResponse?.status === 'fail') { + throw new Error('Error signing transaction'); + } + + const response = { + responses: eckoResponse.quickSignData, + }; + + if ('responses' in response) { + response.responses.map((signedCommand, i) => { + if (signedCommand.outcome.result === 'success') { + if (signedCommand.outcome.hash !== transactionHashes[i]) { + throw new Error( + `Hash of the transaction signed by the wallet does not match. Our hash: ${transactionHashes[i]}, wallet hash: ${signedCommand.outcome.hash}`, + ); + } + + const sigs = signedCommand.commandSigData.sigs.filter( + (sig) => sig.sig !== null, + ) as { pubKey: string; sig: string }[]; + + // Add the signature(s) that we received from the wallet to the PactCommand(s) + transactions[i] = addSignatures(transactions[i], ...sigs); + } + }); + } else { + throw new Error('Error signing transaction'); + } + + return isList ? transactions : transactions[0]; + }) as IEckoSignFunction; + + quicksignWithEckoWallet.isInstalled = isInstalled; + quicksignWithEckoWallet.isConnected = isConnected; + quicksignWithEckoWallet.connect = connect; + quicksignWithEckoWallet.getAccountInfo = getAccountInfo; + + return quicksignWithEckoWallet; +} diff --git a/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts b/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts new file mode 100644 index 0000000000..f97c3200fa --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts @@ -0,0 +1,63 @@ +import { ISingleSignFunction } from '../ISignFunction'; +import { pactCommandToSigningRequest } from '../utils/pactCommandToSigningRequest'; +import { parseTransactionCommand } from '../utils/parseTransactionCommand'; + +import { + connect, + getAccountInfo, + isConnected, + isInstalled, +} from './eckoCommon'; +import { ICommonEckoFunctions, IEckoSignResponse } from './eckoTypes'; + +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Window { + kadena?: { + isKadena: boolean; + request(args: unknown): Promise; + }; + } +} + +interface IEckoSignSingleFunction + extends ISingleSignFunction, + ICommonEckoFunctions {} + +/** + * Creates the signWithEckoWallet function with interface {@link ISingleSignFunction} + * + * @remarks + * It is preferred to use the {@link createEckoWalletQuicksign} function + * + * @public + */ +export function createEckoWalletSign(): IEckoSignSingleFunction { + const signWithEckoWallet: IEckoSignSingleFunction = async (transaction) => { + const parsedTransaction = parseTransactionCommand(transaction); + const signingRequest = pactCommandToSigningRequest(parsedTransaction); + + await connect(parsedTransaction.networkId); + + const response = await window.kadena?.request({ + method: 'kda_requestSign', + data: { + networkId: parsedTransaction.networkId, + signingCmd: signingRequest, + }, + }); + + if (response?.signedCmd === undefined) { + throw new Error('Error signing transaction'); + } + + return response.signedCmd; + }; + + signWithEckoWallet.isInstalled = isInstalled; + signWithEckoWallet.isConnected = isConnected; + signWithEckoWallet.connect = connect; + signWithEckoWallet.getAccountInfo = getAccountInfo; + + return signWithEckoWallet; +} diff --git a/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts b/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts new file mode 100644 index 0000000000..5c6381f972 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts @@ -0,0 +1,195 @@ +/** @jest-environment jsdom */ + +import { IPactCommand } from '../../../interfaces/IPactCommand'; +import { createTransaction } from '../../../utils/createTransaction'; +import { ISignFunction } from '../../ISignFunction'; +import { createEckoWalletQuicksign } from '../quicksignWithEckoWallet'; + +import { TextDecoder, TextEncoder } from 'util'; + +Object.assign(global, { TextDecoder, TextEncoder }); + +const mockEckoRequest = jest.fn(); + +Object.defineProperty(window, 'kadena', { + value: { + isKadena: true, + request: mockEckoRequest, + }, +}); + +describe('quicksignWithEckoWallet', () => { + let transaction: IPactCommand; + let quicksignWithEckoWallet: ISignFunction; + + beforeEach(() => { + mockEckoRequest.mockReset(); + + transaction = { + payload: { + exec: { + code: '(coin.transfer "bonnie" "clyde" 1)', + data: { 'test-data': 'test-data' }, + }, + }, + meta: { + chainId: '1', + gasLimit: 10000, + gasPrice: 1e-8, + sender: 'test-sender', + ttl: 30 * 6, + creationTime: 1234, + }, + signers: [ + { + clist: [ + { + name: 'test-cap-name', + args: ['test-cap-arg'], + }, + ], + pubKey: 'test-pub-key', + }, + ], + networkId: 'testnet-id', + nonce: '', + }; + }); + + it('throws when no transactions are passed', async () => { + const quicksignWithEckoWallet = createEckoWalletQuicksign(); + + try { + // @ts-expect-error - Expected 1 arguments, but got 0. + await quicksignWithEckoWallet(); + // Fail test if signWithWalletConnect() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('No transaction(s) to sign'); + } + }); + + it('signs a transaction', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + quickSignData: [ + { + outcome: { + result: 'success', + hash: 'test-hash', + }, + commandSigData: { + cmd: 'test-cmd', + sigs: [ + { + caps: [ + { + args: ['test-cap-arg'], + name: 'test-cap-name', + }, + ], + pubKey: 'test-pub-key', + sig: 'test-sig', + }, + ], + }, + }, + ], + }); + + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + const unsignedTransaction = createTransaction(transaction); + unsignedTransaction.hash = 'test-hash'; + const result = await quicksignWithEckoWallet(unsignedTransaction); + + expect(window.kadena?.request).toHaveBeenCalledWith({ + method: 'kda_requestQuickSign', + data: { + networkId: 'testnet-id', + commandSigDatas: [ + { + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key"}],"networkId":"testnet-id","nonce":""}', + sigs: [ + { + pubKey: 'test-pub-key', + sig: null, + }, + ], + }, + ], + }, + }); + + expect(result).toEqual({ + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key"}],"networkId":"testnet-id","nonce":""}', + hash: 'test-hash', + sigs: [{ sig: 'test-sig' }], + }); + }); + + it('throws when there is no signing response', async () => { + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + try { + await quicksignWithEckoWallet(createTransaction(transaction)); + // Fail test if quicksignWithEckoWallet() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('Error signing transaction'); + } + }); + + it('throws when there are no responses', async () => { + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + try { + await quicksignWithEckoWallet(createTransaction(transaction)); + // Fail test if quicksignWithEckoWallet() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('Error signing transaction'); + } + }); + + it('throws when the hash of the unsigned and signed transaction do not match', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + quickSignData: [ + { + outcome: { + result: 'success', + hash: 'test-hash-different', + }, + commandSigData: { + cmd: 'test-cmd', + sigs: [ + { + caps: [ + { + args: ['test-cap-arg'], + name: 'test-cap-name', + }, + ], + pubKey: 'test-pub-key', + sig: 'test-sig', + }, + ], + }, + }, + ], + }); + + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + try { + await quicksignWithEckoWallet(createTransaction(transaction)); + // Fail test if quicksignWithEckoWallet() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain( + 'Hash of the transaction signed by the wallet does not match. Our hash', + ); + } + }); +}); diff --git a/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts b/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts new file mode 100644 index 0000000000..23e5e6f819 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts @@ -0,0 +1,169 @@ +/** @jest-environment jsdom */ + +import { + IExecutionPayloadObject, + IPactCommand, +} from '../../../interfaces/IPactCommand'; +import { createTransaction } from '../../../utils/createTransaction'; +import { ISingleSignFunction } from '../../ISignFunction'; +import { createEckoWalletSign } from '../signWithEckoWallet'; + +import { TextDecoder, TextEncoder } from 'util'; + +Object.assign(global, { TextDecoder, TextEncoder }); + +const mockEckoRequest = jest.fn(); + +Object.defineProperty(window, 'kadena', { + value: { + isKadena: true, + request: mockEckoRequest, + }, +}); + +describe('signWithEckoWallet', () => { + let transaction: IPactCommand & { payload: IExecutionPayloadObject }; + let signWithEckoWallet: ISingleSignFunction; + + beforeEach(() => { + mockEckoRequest.mockReset(); + + transaction = { + payload: { + exec: { + code: '(coin.transfer "bonnie" "clyde" 1)', + data: { + test: 'test-data', + }, + }, + }, + meta: { + chainId: '0', + gasLimit: 2300, + gasPrice: 0.00000001, + sender: 'test-sender', + ttl: 3600, + creationTime: 123456789, + }, + signers: [ + { + pubKey: '', + clist: [ + { + name: 'cap.test-cap-name', + args: ['test-cap-arg'], + }, + ], + }, + ], + networkId: 'test-network-id', + nonce: 'kjs-test', + }; + }); + + it('signs a transaction', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + signedCmd: { cmd: 'test-cmd', sigs: [{ sig: 'test-sig' }] }, + }); + + signWithEckoWallet = createEckoWalletSign(); + + const signedTransaction = await signWithEckoWallet( + createTransaction(transaction), + ); + + expect(mockEckoRequest).toHaveBeenCalledWith({ + method: 'kda_requestSign', + data: { + networkId: 'test-network-id', + signingCmd: { + code: transaction.payload.exec.code, + data: transaction.payload.exec.data, + caps: [ + { + role: 'test-cap-name', + description: 'Description for cap.test-cap-name', + cap: { + name: 'cap.test-cap-name', + args: ['test-cap-arg'], + }, + }, + ], + nonce: transaction.nonce, + chainId: transaction.meta.chainId, + gasLimit: transaction.meta.gasLimit, + gasPrice: transaction.meta.gasPrice, + sender: transaction.meta.sender, + ttl: transaction.meta.ttl, + }, + }, + }); + + expect(signedTransaction.cmd).toBe('test-cmd'); + + expect(signedTransaction).toEqual({ + cmd: 'test-cmd', + sigs: [{ sig: 'test-sig' }], + }); + }); + + it('throws when there is no signing response', async () => { + signWithEckoWallet = createEckoWalletSign(); + + //@ts-expect-error The operand of a 'delete' operator must be optional. + delete transaction.payload.exec; + + try { + await signWithEckoWallet(createTransaction(transaction)); + // Fail test if signWithWalletConnect() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('`cont` transactions are not supported'); + } + }); + + it('adds an empty clist when signer.clist is undefined', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + signedCmd: { cmd: 'test-cmd', sigs: [{ sig: 'test-sig' }] }, + }); + + signWithEckoWallet = createEckoWalletSign(); + + delete transaction.signers[0].clist; + + await signWithEckoWallet(createTransaction(transaction)); + + expect(window.kadena?.request).toHaveBeenCalledWith({ + method: 'kda_requestSign', + data: { + networkId: 'test-network-id', + signingCmd: { + code: transaction.payload.exec.code, + data: transaction.payload.exec.data, + caps: [], + nonce: transaction.nonce, + chainId: transaction.meta.chainId, + gasLimit: transaction.meta.gasLimit, + gasPrice: transaction.meta.gasPrice, + sender: transaction.meta.sender, + ttl: transaction.meta.ttl, + }, + }, + }); + }); + + it('throws when signing cont command', async () => { + signWithEckoWallet = createEckoWalletSign(); + + try { + await signWithEckoWallet(createTransaction(transaction)); + // Fail test if signWithEckoWallet() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + console.log(e); + expect(e.message).toContain('Error signing transaction'); + } + }); +}); diff --git a/packages/libs/client/src/signing/index.ts b/packages/libs/client/src/signing/index.ts index 12a23417a6..84c2fde79c 100644 --- a/packages/libs/client/src/signing/index.ts +++ b/packages/libs/client/src/signing/index.ts @@ -1,8 +1,11 @@ export { TWalletConnectChainId } from './walletconnect/walletConnectTypes'; export { ISingleSignFunction, ISignFunction } from './ISignFunction'; -export * from './chainweaver/signWithChainweaver'; export * from './utils/isSignedTransaction'; export * from './utils/addSignatures'; + +export * from './chainweaver/signWithChainweaver'; +export * from './eckoWallet/signWithEckoWallet'; +export * from './eckoWallet/quicksignWithEckoWallet'; export * from './walletconnect/signWithWalletConnect'; export * from './walletconnect/quicksignWithWalletConnect'; diff --git a/packages/libs/client/src/signing/utils/pactCommandToSigningRequest.ts b/packages/libs/client/src/signing/utils/pactCommandToSigningRequest.ts new file mode 100644 index 0000000000..50631b9931 --- /dev/null +++ b/packages/libs/client/src/signing/utils/pactCommandToSigningRequest.ts @@ -0,0 +1,39 @@ +import { IPactCommand } from '../../interfaces/IPactCommand'; +import { isExecCommand } from '../../interfaces/isExecCommand'; +import { ISigningRequest } from '../../interfaces/ISigningRequest'; + +export const pactCommandToSigningRequest = ( + parsedTransaction: IPactCommand, +): ISigningRequest => { + if (!isExecCommand(parsedTransaction)) { + throw new Error('`cont` transactions are not supported'); + } + + return { + code: parsedTransaction.payload.exec.code ?? '', + data: parsedTransaction.payload.exec.data as { [key: string]: unknown }, + caps: parsedTransaction.signers.flatMap((signer) => { + if (signer.clist === undefined) { + return []; + } + return signer.clist.map(({ name, args }) => { + const nameArr = name.split('.'); + + return { + role: nameArr[nameArr.length - 1], + description: `Description for ${name}`, + cap: { + name, + args, + }, + }; + }); + }), + nonce: parsedTransaction.nonce, + chainId: parsedTransaction.meta.chainId, + gasLimit: parsedTransaction.meta.gasLimit, + gasPrice: parsedTransaction.meta.gasPrice, + sender: parsedTransaction.meta.sender, + ttl: parsedTransaction.meta.ttl, + }; +}; diff --git a/packages/libs/client/src/signing/walletconnect/signWithWalletConnect.ts b/packages/libs/client/src/signing/walletconnect/signWithWalletConnect.ts index 45e4bb421d..02aac768b8 100644 --- a/packages/libs/client/src/signing/walletconnect/signWithWalletConnect.ts +++ b/packages/libs/client/src/signing/walletconnect/signWithWalletConnect.ts @@ -1,10 +1,11 @@ import { ICommand, IUnsignedCommand } from '@kadena/types'; import { isExecCommand } from '../../interfaces/isExecCommand'; +import { ISigningRequest } from '../../interfaces/ISigningRequest'; import { ISingleSignFunction } from '../ISignFunction'; import { parseTransactionCommand } from '../utils/parseTransactionCommand'; -import { ISigningRequest, TWalletConnectChainId } from './walletConnectTypes'; +import { TWalletConnectChainId } from './walletConnectTypes'; import Client from '@walletconnect/sign-client'; import { SessionTypes } from '@walletconnect/types'; diff --git a/packages/libs/client/src/signing/walletconnect/walletConnectTypes.ts b/packages/libs/client/src/signing/walletconnect/walletConnectTypes.ts index d155f9b44e..997c9751ea 100644 --- a/packages/libs/client/src/signing/walletconnect/walletConnectTypes.ts +++ b/packages/libs/client/src/signing/walletconnect/walletConnectTypes.ts @@ -1,27 +1,7 @@ import { ChainwebChainId } from '@kadena/chainweb-node-client'; -import { ISigningCap } from '@kadena/types'; import { IPactCommand } from '../../interfaces/IPactCommand'; -/** - * This is the interface for the signing request that is sent to the wallet. - * It differs from the type in @kadena/types. When that is updated, we should - * use that type instead. - * @internal - */ -export interface ISigningRequest { - code: string; - data?: Record; - caps: ISigningCap[]; - nonce?: string; - chainId?: ChainwebChainId; - gasLimit?: number; - gasPrice?: number; - ttl?: number; - sender?: string; - extraSigners?: string[]; -} - /** * @internal */ From befc4c08fd419c2455c8c23cf095ac8b47aaf7e7 Mon Sep 17 00:00:00 2001 From: Ashwin van Dijk Date: Tue, 8 Aug 2023 13:27:31 +0200 Subject: [PATCH 2/6] Remove getAccountInfo, add more tests --- .../src/signing/eckoWallet/eckoCommon.ts | 11 +- .../src/signing/eckoWallet/eckoTypes.ts | 9 ++ .../eckoWallet/quicksignWithEckoWallet.ts | 23 +-- .../signing/eckoWallet/signWithEckoWallet.ts | 15 +- .../eckoWallet/tests/eckoCommon.test.ts | 149 ++++++++++++++++++ .../tests/quicksignWithEckoWallet.test.ts | 147 +++++++++++++++++ .../tests/signWithEckoWallet.test.ts | 4 + .../src/utils/tests/createTransaction.test.ts | 1 + 8 files changed, 318 insertions(+), 41 deletions(-) create mode 100644 packages/libs/client/src/signing/eckoWallet/tests/eckoCommon.test.ts diff --git a/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts index 12219a0d61..9b49132327 100644 --- a/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts +++ b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts @@ -1,5 +1,5 @@ import { - IEckoAccountsResponse, + ICommonEckoFunctions, IEckoConnectOrStatusResponse, } from './eckoTypes'; @@ -49,12 +49,3 @@ export const connect: ICommonEckoFunctions['connect'] = async (networkId) => { return true; }; - -export const getAccountInfo = async ( - networkId: string, -): Promise => { - return window.kadena?.request({ - method: 'kda_requestAccount', - networkId, - }); -}; diff --git a/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts index a6bd824ce1..651fc4b9b8 100644 --- a/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts +++ b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts @@ -1,6 +1,7 @@ import { ICommand } from '@kadena/types'; import { IQuicksignResponseOutcomes } from '../../signing-api/v1/quicksign'; +import { ISignFunction, ISingleSignFunction } from '../ISignFunction'; export type EckoStatus = 'success' | 'fail'; @@ -10,6 +11,14 @@ export interface ICommonEckoFunctions { connect: (networkId: string) => Promise; } +export interface IEckoSignSingleFunction + extends ISingleSignFunction, + ICommonEckoFunctions {} + +export interface IEckoSignFunction + extends ISignFunction, + ICommonEckoFunctions {} + export interface IEckoConnectOrStatusResponse { status: EckoStatus; message?: string; diff --git a/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts b/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts index 7eb3c8d7f6..0f21f18c23 100644 --- a/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts +++ b/packages/libs/client/src/signing/eckoWallet/quicksignWithEckoWallet.ts @@ -1,18 +1,10 @@ import { ICommand, IUnsignedCommand } from '@kadena/types'; -import { ISignFunction } from '../ISignFunction'; -import { addSignatures } from '../utils/addSignature'; +import { addSignatures } from '../utils/addSignatures'; import { parseTransactionCommand } from '../utils/parseTransactionCommand'; -import { - connect, - getAccountInfo, - isConnected, - isInstalled, -} from './eckoCommon'; -import { ICommonEckoFunctions, IEckoQuicksignResponse } from './eckoTypes'; - -interface IEckoSignFunction extends ISignFunction, ICommonEckoFunctions {} +import { connect, isConnected, isInstalled } from './eckoCommon'; +import { IEckoQuicksignResponse, IEckoSignFunction } from './eckoTypes'; /** * Creates the quicksignWithWalletConnect function with interface {@link ISingleSignFunction} @@ -63,12 +55,8 @@ export function createEckoWalletQuicksign(): IEckoSignFunction { throw new Error('Error signing transaction'); } - const response = { - responses: eckoResponse.quickSignData, - }; - - if ('responses' in response) { - response.responses.map((signedCommand, i) => { + if ('quickSignData' in eckoResponse) { + eckoResponse.quickSignData.map((signedCommand, i) => { if (signedCommand.outcome.result === 'success') { if (signedCommand.outcome.hash !== transactionHashes[i]) { throw new Error( @@ -94,7 +82,6 @@ export function createEckoWalletQuicksign(): IEckoSignFunction { quicksignWithEckoWallet.isInstalled = isInstalled; quicksignWithEckoWallet.isConnected = isConnected; quicksignWithEckoWallet.connect = connect; - quicksignWithEckoWallet.getAccountInfo = getAccountInfo; return quicksignWithEckoWallet; } diff --git a/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts b/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts index f97c3200fa..6cb6644f2c 100644 --- a/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts +++ b/packages/libs/client/src/signing/eckoWallet/signWithEckoWallet.ts @@ -1,14 +1,8 @@ -import { ISingleSignFunction } from '../ISignFunction'; import { pactCommandToSigningRequest } from '../utils/pactCommandToSigningRequest'; import { parseTransactionCommand } from '../utils/parseTransactionCommand'; -import { - connect, - getAccountInfo, - isConnected, - isInstalled, -} from './eckoCommon'; -import { ICommonEckoFunctions, IEckoSignResponse } from './eckoTypes'; +import { connect, isConnected, isInstalled } from './eckoCommon'; +import { IEckoSignResponse, IEckoSignSingleFunction } from './eckoTypes'; declare global { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -20,10 +14,6 @@ declare global { } } -interface IEckoSignSingleFunction - extends ISingleSignFunction, - ICommonEckoFunctions {} - /** * Creates the signWithEckoWallet function with interface {@link ISingleSignFunction} * @@ -57,7 +47,6 @@ export function createEckoWalletSign(): IEckoSignSingleFunction { signWithEckoWallet.isInstalled = isInstalled; signWithEckoWallet.isConnected = isConnected; signWithEckoWallet.connect = connect; - signWithEckoWallet.getAccountInfo = getAccountInfo; return signWithEckoWallet; } diff --git a/packages/libs/client/src/signing/eckoWallet/tests/eckoCommon.test.ts b/packages/libs/client/src/signing/eckoWallet/tests/eckoCommon.test.ts new file mode 100644 index 0000000000..aab7258573 --- /dev/null +++ b/packages/libs/client/src/signing/eckoWallet/tests/eckoCommon.test.ts @@ -0,0 +1,149 @@ +/** @jest-environment jsdom */ + +import { connect, isConnected, isInstalled } from '../eckoCommon'; + +import { TextDecoder, TextEncoder } from 'util'; + +Object.assign(global, { TextDecoder, TextEncoder }); + +const mockEckoRequest = jest.fn(); + +Object.defineProperty(window, 'kadena', { + value: { + isKadena: true, + request: mockEckoRequest, + }, + writable: true, +}); + +describe('eckoCommon', () => { + beforeEach(() => { + mockEckoRequest.mockReset(); + + window.kadena = { + request: mockEckoRequest, + isKadena: true, + }; + }); + + describe('isInstalled()', () => { + it('returns true when Ecko Wallet is installed', () => { + const result = isInstalled(); + + expect(result).toBeTruthy(); + }); + + it('returns false when Ecko Wallet is NOT installed', () => { + window.kadena = { + request: mockEckoRequest, + isKadena: false, + }; + + const result = isInstalled(); + + expect(result).toBeFalsy(); + }); + }); + + describe('isConnected()', () => { + it('returns false when Ecko Wallet is not installed', async () => { + window.kadena = { + request: mockEckoRequest, + isKadena: false, + }; + + const result = await isConnected('testnet04'); + + expect(result).toBeFalsy(); + }); + + it('returns true when already connected', async () => { + // mock kda_checkStatus + mockEckoRequest.mockResolvedValueOnce({ + status: 'success', + }); + + const result = await isConnected('testnet04'); + + expect(result).toBeTruthy(); + }); + }); + + describe('connect()', () => { + it('throws when Ecko Wallet is not installed', async () => { + window.kadena = { + request: mockEckoRequest, + isKadena: false, + }; + + try { + await connect('testnet04'); + // Fail test if connect() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('Ecko Wallet is not installed'); + } + }); + + it('returns true when already connected', async () => { + // mock kda_checkStatus + mockEckoRequest.mockResolvedValueOnce({ + status: 'success', + }); + + const result = await connect('testnet04'); + + expect(result).toBeTruthy(); + }); + + it('connects when not connected yet', async () => { + // mock kda_checkStatus + mockEckoRequest.mockResolvedValueOnce({ + status: 'fail', + }); + + // mock kda_connect + mockEckoRequest.mockResolvedValueOnce({ + status: 'success', + }); + + const result = await connect('testnet04'); + + expect(mockEckoRequest).toHaveBeenCalledWith({ + method: 'kda_checkStatus', + networkId: 'testnet04', + }); + + expect(mockEckoRequest).toHaveBeenCalledWith({ + method: 'kda_connect', + networkId: 'testnet04', + }); + + expect(result).toBeTruthy(); + }); + + it('throws when the user declines the connection', async () => { + window.kadena = { + request: mockEckoRequest, + isKadena: true, + }; + // mock kda_checkStatus + mockEckoRequest.mockResolvedValueOnce({ + status: 'fail', + }); + + // mock kda_connect + mockEckoRequest.mockResolvedValueOnce({ + status: 'fail', + }); + + try { + await connect('testnet04'); + // Fail test if connect() doesn't throw. Next line shouldn't be reached. + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('User declined connection'); + } + }); + }); +}); diff --git a/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts b/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts index 5c6381f972..6448316f64 100644 --- a/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts +++ b/packages/libs/client/src/signing/eckoWallet/tests/quicksignWithEckoWallet.test.ts @@ -56,6 +56,10 @@ describe('quicksignWithEckoWallet', () => { }; }); + afterAll(() => { + mockEckoRequest.mockRestore(); + }); + it('throws when no transactions are passed', async () => { const quicksignWithEckoWallet = createEckoWalletQuicksign(); @@ -128,6 +132,119 @@ describe('quicksignWithEckoWallet', () => { }); }); + it('signs multiple transactions', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + quickSignData: [ + { + outcome: { + result: 'success', + hash: 'test-hash', + }, + commandSigData: { + cmd: 'test-cmd', + sigs: [ + { + caps: [ + { + args: ['test-cap-arg'], + name: 'test-cap-name', + }, + ], + pubKey: 'test-pub-key', + sig: 'test-sig', + }, + ], + }, + }, + { + outcome: { + result: 'success', + hash: 'test-hash-2', + }, + commandSigData: { + cmd: 'test-cmd-2', + sigs: [ + { + caps: [ + { + args: ['test-cap-arg'], + name: 'test-cap-name', + }, + ], + pubKey: 'test-pub-key-2', + sig: 'test-sig-2', + }, + ], + }, + }, + ], + }); + + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + const unsignedTransactions = [ + createTransaction(transaction), + createTransaction({ + ...transaction, + signers: [ + { + clist: [ + { + name: 'test-cap-name', + args: ['test-cap-arg'], + }, + ], + pubKey: 'test-pub-key-2', + }, + ], + }), + ]; + unsignedTransactions[0].hash = 'test-hash'; + unsignedTransactions[1].hash = 'test-hash-2'; + const result = await quicksignWithEckoWallet(unsignedTransactions); + + expect(window.kadena?.request).toHaveBeenCalledWith({ + method: 'kda_requestQuickSign', + data: { + networkId: 'testnet-id', + commandSigDatas: [ + { + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key"}],"networkId":"testnet-id","nonce":""}', + sigs: [ + { + pubKey: 'test-pub-key', + sig: null, + }, + ], + }, + { + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key-2"}],"networkId":"testnet-id","nonce":""}', + sigs: [ + { + pubKey: 'test-pub-key-2', + sig: null, + }, + ], + }, + ], + }, + }); + + expect(result).toEqual([ + { + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key"}],"networkId":"testnet-id","nonce":""}', + hash: 'test-hash', + sigs: [{ sig: 'test-sig' }], + }, + { + cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"bonnie\\" \\"clyde\\" 1)","data":{"test-data":"test-data"}}},"meta":{"chainId":"1","gasLimit":10000,"gasPrice":1e-8,"sender":"test-sender","ttl":180,"creationTime":1234},"signers":[{"clist":[{"name":"test-cap-name","args":["test-cap-arg"]}],"pubKey":"test-pub-key-2"}],"networkId":"testnet-id","nonce":""}', + hash: 'test-hash-2', + sigs: [{ sig: 'test-sig-2' }], + }, + ]); + }); + it('throws when there is no signing response', async () => { quicksignWithEckoWallet = createEckoWalletQuicksign(); @@ -140,6 +257,22 @@ describe('quicksignWithEckoWallet', () => { } }); + it('throws when there is no quickSignData in the response from Ecko', async () => { + mockEckoRequest.mockResolvedValue({ + status: 'success', + }); + + quicksignWithEckoWallet = createEckoWalletQuicksign(); + const unsignedTransaction = createTransaction(transaction); + + try { + await quicksignWithEckoWallet(unsignedTransaction); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toContain('Error signing transaction'); + } + }); + it('throws when there are no responses', async () => { quicksignWithEckoWallet = createEckoWalletQuicksign(); @@ -192,4 +325,18 @@ describe('quicksignWithEckoWallet', () => { ); } }); + + it('throws when the networks of the transactions are not the same', async () => { + quicksignWithEckoWallet = createEckoWalletQuicksign(); + + try { + await quicksignWithEckoWallet([ + createTransaction(transaction), + createTransaction({ ...transaction, networkId: 'testnet-id-2' }), + ]); + expect(true).toBe(false); + } catch (e) { + expect(e.message).toBe('Network is not equal for all transactions'); + } + }); }); diff --git a/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts b/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts index 23e5e6f819..5350911be4 100644 --- a/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts +++ b/packages/libs/client/src/signing/eckoWallet/tests/signWithEckoWallet.test.ts @@ -61,6 +61,10 @@ describe('signWithEckoWallet', () => { }; }); + afterAll(() => { + mockEckoRequest.mockRestore(); + }); + it('signs a transaction', async () => { mockEckoRequest.mockResolvedValue({ status: 'success', diff --git a/packages/libs/client/src/utils/tests/createTransaction.test.ts b/packages/libs/client/src/utils/tests/createTransaction.test.ts index 52751d540f..b4b7d1fa50 100644 --- a/packages/libs/client/src/utils/tests/createTransaction.test.ts +++ b/packages/libs/client/src/utils/tests/createTransaction.test.ts @@ -48,6 +48,7 @@ describe('createTransaction', () => { expect(transaction.sigs).toHaveLength(1); expect(transaction.sigs).toStrictEqual([undefined]); }); + it('returns a transaction object with empty sigs array if there is no signers in the pactCommand', () => { const { signers, ...command } = pactCommand; From 889c25337b0ffd42a4207528e07b4428fc05f8c5 Mon Sep 17 00:00:00 2001 From: Ashwin van Dijk Date: Tue, 8 Aug 2023 13:55:59 +0200 Subject: [PATCH 3/6] Update to client 1.0.0 --- .../feat-signWithEckoWallet_2023-08-08-11-29.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@kadena/client/feat-signWithEckoWallet_2023-08-08-11-29.json diff --git a/common/changes/@kadena/client/feat-signWithEckoWallet_2023-08-08-11-29.json b/common/changes/@kadena/client/feat-signWithEckoWallet_2023-08-08-11-29.json new file mode 100644 index 0000000000..328c8862f1 --- /dev/null +++ b/common/changes/@kadena/client/feat-signWithEckoWallet_2023-08-08-11-29.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@kadena/client", + "comment": "Adds signWithEckoWallet and quickSignWithEckoWallet", + "type": "minor" + } + ], + "packageName": "@kadena/client" +} From 0c8c6ae07779452bda81575009e70610b1ce8692 Mon Sep 17 00:00:00 2001 From: Ashwin van Dijk Date: Wed, 16 Aug 2023 10:35:17 +0200 Subject: [PATCH 4/6] PR comments --- packages/libs/client/src/signing/eckoWallet/eckoCommon.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts index 9b49132327..4d64c5a700 100644 --- a/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts +++ b/packages/libs/client/src/signing/eckoWallet/eckoCommon.ts @@ -5,7 +5,7 @@ import { export const isInstalled: ICommonEckoFunctions['isInstalled'] = () => { const { kadena } = window; - return Boolean(kadena && kadena.isKadena); + return Boolean(kadena && kadena.isKadena && kadena.request); }; export const isConnected: ICommonEckoFunctions['isConnected'] = async ( @@ -21,11 +21,7 @@ export const isConnected: ICommonEckoFunctions['isConnected'] = async ( networkId, }); - if (checkStatusResponse?.status === 'fail') { - return false; - } - - return true; + return checkStatusResponse?.status === 'success'; }; export const connect: ICommonEckoFunctions['connect'] = async (networkId) => { From 89c02a49aee886ca47ce9ef26fc320264e7e8055 Mon Sep 17 00:00:00 2001 From: Ashwin van Dijk Date: Wed, 16 Aug 2023 17:27:21 +0200 Subject: [PATCH 5/6] Export types --- packages/libs/client/etc/client.api.md | 22 +++++++++++++++---- .../libs/client/src/signing/ISignFunction.ts | 4 ++-- .../src/signing/eckoWallet/eckoTypes.ts | 13 ++++++++++- packages/libs/client/src/signing/index.ts | 5 +++++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/packages/libs/client/etc/client.api.md b/packages/libs/client/etc/client.api.md index a38df96b4e..9bc6ec53bd 100644 --- a/packages/libs/client/etc/client.api.md +++ b/packages/libs/client/etc/client.api.md @@ -30,13 +30,9 @@ export { ChainId } // @public export const createClient: ICreateClient; -// Warning: (ae-forgotten-export) The symbol "IEckoSignFunction" needs to be exported by the entry point index.d.ts -// // @public export function createEckoWalletQuicksign(): IEckoSignFunction; -// Warning: (ae-forgotten-export) The symbol "IEckoSignSingleFunction" needs to be exported by the entry point index.d.ts -// // @public export function createEckoWalletSign(): IEckoSignSingleFunction; @@ -102,6 +98,16 @@ export { ICommand } export { ICommandResult } +// @public +export interface ICommonEckoFunctions { + // (undocumented) + connect: (networkId: string) => Promise; + // (undocumented) + isConnected: (networkId: string) => Promise; + // (undocumented) + isInstalled: () => boolean; +} + // @public export interface IContinuationPayloadObject { // (undocumented) @@ -123,6 +129,14 @@ export interface ICreateClient { }) => string): IClient; } +// @public +export interface IEckoSignFunction extends ISignFunction, ICommonEckoFunctions { +} + +// @public +export interface IEckoSignSingleFunction extends ISingleSignFunction, ICommonEckoFunctions { +} + // @public export interface IExecutionPayloadObject { // (undocumented) diff --git a/packages/libs/client/src/signing/ISignFunction.ts b/packages/libs/client/src/signing/ISignFunction.ts index 6d9af11843..de0daf549b 100644 --- a/packages/libs/client/src/signing/ISignFunction.ts +++ b/packages/libs/client/src/signing/ISignFunction.ts @@ -1,7 +1,7 @@ import { ICommand, IUnsignedCommand } from '@kadena/types'; /** - * Interface to use when writing a singing function that accepts multiple transactions + * Interface to use when writing a signing function that accepts a single transaction * @public */ export interface ISingleSignFunction { @@ -9,7 +9,7 @@ export interface ISingleSignFunction { } /** - * Interface to use when writing a singing function that accepts multiple transactions + * Interface to use when writing a signing function that accepts multiple transactions * @public */ export interface ISignFunction extends ISingleSignFunction { diff --git a/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts index 651fc4b9b8..b4e56a3e70 100644 --- a/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts +++ b/packages/libs/client/src/signing/eckoWallet/eckoTypes.ts @@ -5,16 +5,27 @@ import { ISignFunction, ISingleSignFunction } from '../ISignFunction'; export type EckoStatus = 'success' | 'fail'; +/** + * Interface that describes the common functions to be used with Ecko Wallet + * @public + */ export interface ICommonEckoFunctions { isInstalled: () => boolean; isConnected: (networkId: string) => Promise; connect: (networkId: string) => Promise; } - +/** + * Interface to use when writing a signing function for Ecko Wallet that accepts a single transaction + * @public + */ export interface IEckoSignSingleFunction extends ISingleSignFunction, ICommonEckoFunctions {} +/** + * Interface to use when writing a signing function for Ecko Wallet that accepts multiple transactions + * @public + */ export interface IEckoSignFunction extends ISignFunction, ICommonEckoFunctions {} diff --git a/packages/libs/client/src/signing/index.ts b/packages/libs/client/src/signing/index.ts index 84c2fde79c..a1c4b9cadd 100644 --- a/packages/libs/client/src/signing/index.ts +++ b/packages/libs/client/src/signing/index.ts @@ -1,3 +1,8 @@ +export { + ICommonEckoFunctions, + IEckoSignSingleFunction, + IEckoSignFunction, +} from './eckoWallet/eckoTypes'; export { TWalletConnectChainId } from './walletconnect/walletConnectTypes'; export { ISingleSignFunction, ISignFunction } from './ISignFunction'; From 8748d4b134278ef8dae9817c1ba7430bbba4ef20 Mon Sep 17 00:00:00 2001 From: Albert G <516972+alber70g@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:48:57 +0200 Subject: [PATCH 6/6] chore(client): adds changeset --- .changeset/neat-kiwis-own.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/neat-kiwis-own.md diff --git a/.changeset/neat-kiwis-own.md b/.changeset/neat-kiwis-own.md new file mode 100644 index 0000000000..c0986f1278 --- /dev/null +++ b/.changeset/neat-kiwis-own.md @@ -0,0 +1,7 @@ +--- +'@kadena/client': minor +--- + +Add `createEckoWalletSign()` and `createEckoWalletQuicksign()`. This creates a +wrapper for the +[eckoWALLET API](https://docs.ecko.finance/eckodex/getting-started/eckowallet/eckowallet-api)