diff --git a/src/wallet.test.ts b/src/wallet.test.ts index 96c32f1a..4c875a77 100644 --- a/src/wallet.test.ts +++ b/src/wallet.test.ts @@ -414,3 +414,94 @@ describe('wallet', () => { }); }); }); + +describe('getPlumeSignature', () => { + it('should sign with a valid address', async () => { + const { engine } = createTestSetup(); + const getAccounts = async () => testAddresses.slice(); + const witnessedMsgParams: MessageParams[] = []; + const processPlumeSignature = async (msgParams: MessageParams) => { + witnessedMsgParams.push(msgParams); + return testMsgSig; + }; + + engine.push(createWalletMiddleware({ getAccounts, processPlumeSignature })); + const message = [ + { + type: 'string', + name: 'message', + value: 'Hi, Alice!', + }, + ]; + + const payload = { + method: 'eth_getPlumeSignature', + params: [message, testAddresses[0]], + }; + const signMsgResponse = await pify(engine.handle).call(engine, payload); + const signMsgResult = signMsgResponse.result; + + expect(signMsgResult).toBeDefined(); + expect(signMsgResult).toStrictEqual(testMsgSig); + expect(witnessedMsgParams).toHaveLength(1); + expect(witnessedMsgParams[0]).toStrictEqual({ + from: testAddresses[0], + data: message, + }); + }); + + it('should throw with invalid address', async () => { + const { engine } = createTestSetup(); + const getAccounts = async () => testAddresses.slice(); + const witnessedMsgParams: MessageParams[] = []; + const processPlumeSignature = async (msgParams: MessageParams) => { + witnessedMsgParams.push(msgParams); + return testMsgSig; + }; + + engine.push(createWalletMiddleware({ getAccounts, processPlumeSignature })); + const message = [ + { + type: 'string', + name: 'message', + value: 'Hi, Alice!', + }, + ]; + + const payload = { + method: 'eth_getPlumeSignature', + params: [message, '0x3d'], + }; + await expect(pify(engine.handle).call(engine, payload)).rejects.toThrow( + new Error('Invalid parameters: must provide an Ethereum address.'), + ); + }); + + it('should throw with unknown address', async () => { + const { engine } = createTestSetup(); + const getAccounts = async () => testAddresses.slice(); + const witnessedMsgParams: MessageParams[] = []; + const processPlumeSignature = async (msgParams: MessageParams) => { + witnessedMsgParams.push(msgParams); + return testMsgSig; + }; + + engine.push(createWalletMiddleware({ getAccounts, processPlumeSignature })); + const message = [ + { + type: 'string', + name: 'message', + value: 'Hi, Alice!', + }, + ]; + + const payload = { + method: 'eth_getPlumeSignature', + params: [message, testUnkownAddress], + }; + const promise = pify(engine.handle).call(engine, payload); + await expect(promise).rejects.toThrow( + 'The requested account and/or method has not been authorized by the user.', + ); + }); +}); diff --git a/src/wallet.ts b/src/wallet.ts index bbfa6ba9..3a002c2b 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -65,6 +65,10 @@ export interface WalletMiddlewareOptions { req: JsonRpcRequest, version: string, ) => Promise; + processPlumeSignature?: ( + msgParams: MessageParams, + req: JsonRpcRequest, + ) => Promise; } export function createWalletMiddleware({ @@ -78,6 +82,7 @@ export function createWalletMiddleware({ processTypedMessage, processTypedMessageV3, processTypedMessageV4, + processPlumeSignature, }: WalletMiddlewareOptions): JsonRpcMiddleware { if (!getAccounts) { throw new Error('opts.getAccounts is required'); @@ -99,6 +104,7 @@ export function createWalletMiddleware({ eth_getEncryptionPublicKey: createAsyncMiddleware(encryptionPublicKey), eth_decrypt: createAsyncMiddleware(decryptMessage), personal_ecRecover: createAsyncMiddleware(personalRecover), + eth_getPlumeSignature: createAsyncMiddleware(plumeSignature), }); // @@ -353,6 +359,30 @@ export function createWalletMiddleware({ res.result = await processDecryptMessage(msgParams, req); } + async function plumeSignature( + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + ): Promise { + if (!processPlumeSignature) { + throw ethErrors.rpc.methodNotSupported(); + } + + const message: string = (req.params as string[])[0]; + const address: string = await validateAndNormalizeKeyholder( + (req.params as string[])[1], + req, + ); + const extraParams: Record = + (req.params as Record[])[2] || {}; + const msgParams: MessageParams = { + ...extraParams, + from: address, + data: message, + }; + + res.result = await processPlumeSignature(msgParams, req); + } + // // utility //