Skip to content

Commit 5c56abf

Browse files
feat: detect payload signing type for WalletConnect
1 parent fe75b0b commit 5c56abf

File tree

3 files changed

+55
-17
lines changed

3 files changed

+55
-17
lines changed

apps/web/src/components/WalletConnect/useHandleWcRequest.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { SigningType } from "@airgap/beacon-wallet";
1+
import { type SigningType } from "@airgap/beacon-wallet";
22
import { useToast } from "@chakra-ui/react";
33
import { useDynamicModalContext } from "@umami/components";
44
import {
55
type ImplicitAccount,
66
estimate,
7+
getSigningTypeFromPayload,
78
toAccountOperations,
89
} from "@umami/core";
910
import {
@@ -99,14 +100,16 @@ export const useHandleWcRequest = () => {
99100
session
100101
);
101102
}
103+
104+
const signingType: SigningType = getSigningTypeFromPayload(request.params.payload);
102105
const signPayloadProps: SignPayloadProps = {
103106
appName: session.peer.metadata.name,
104107
appIcon: session.peer.metadata.icons[0],
105108
payload: request.params.payload,
106109
isScam: event.verifyContext.verified.isScam,
107110
validationStatus: event.verifyContext.verified.validation,
108111
signer: signer,
109-
signingType: SigningType.RAW,
112+
signingType: signingType,
110113
requestId: { sdkType: "walletconnect", id: id, topic },
111114
};
112115

packages/core/src/decodeBeaconPayload.test.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { SigningType } from "@airgap/beacon-wallet";
22

3-
import { decodeBeaconPayload } from "./decodeBeaconPayload";
3+
import { decodeBeaconPayload, getSigningTypeFromPayload } from "./decodeBeaconPayload";
44

55
describe("decodeBeaconPayload", () => {
66
it("returns an error message if the payload is not a valid utf8", () => {
77
const payload =
88
"05010000004354657a6f73205369676e6564204d6573736167653a20496e76616c696420555446383a20c380c2afc3bfc3bec3bd3b204e6f6e7072696e7461626c653a20000115073b";
99

10+
expect(getSigningTypeFromPayload(payload)).toEqual(SigningType.MICHELINE);
1011
expect(decodeBeaconPayload(payload, SigningType.MICHELINE)).toEqual({
1112
result: payload,
1213
error: "Cannot parse Beacon payload",
@@ -18,6 +19,7 @@ describe("decodeBeaconPayload", () => {
1819
"05010000004254657a6f73205369676e6564204d6573736167653a206d79646170702e636f6d20323032312d30312d31345431353a31363a30345a2048656c6c6f20776f726c6421";
1920
const expected = "Tezos Signed Message: mydapp.com 2021-01-14T15:16:04Z Hello world!";
2021

22+
expect(getSigningTypeFromPayload(payload)).toEqual(SigningType.MICHELINE);
2123
expect(decodeBeaconPayload(payload, SigningType.MICHELINE)).toEqual({ result: expected });
2224
});
2325

@@ -41,6 +43,7 @@ describe("decodeBeaconPayload", () => {
4143
const expected = {
4244
result: "Tezos Signed Message: mydapp.com 2021-01-14T15:16:04Z Hello world!",
4345
};
46+
expect(getSigningTypeFromPayload(payload)).toEqual(SigningType.RAW);
4447
expect(decodeBeaconPayload(payload, SigningType.RAW)).toEqual(expected);
4548
});
4649

@@ -63,6 +66,7 @@ describe("decodeBeaconPayload", () => {
6366
],
6467
});
6568

69+
expect(getSigningTypeFromPayload(raw)).toEqual(SigningType.RAW);
6670
expect(decodeBeaconPayload(raw, SigningType.RAW)).toEqual({ result });
6771
});
6872

@@ -83,16 +87,26 @@ describe("decodeBeaconPayload", () => {
8387

8488
it("handles an empty payload", () => {
8589
const emptyPayload = "";
90+
expect(getSigningTypeFromPayload(emptyPayload)).toEqual(SigningType.RAW);
8691
expect(decodeBeaconPayload(emptyPayload, SigningType.RAW)).toEqual({
87-
result: "",
92+
result: emptyPayload,
8893
});
8994
});
9095

9196
it("handles a payload with non-hex characters", () => {
92-
const nonHexPayload = "0501ZZZZ";
93-
expect(decodeBeaconPayload(nonHexPayload, SigningType.RAW)).toEqual({
97+
const nonHexPayload = "0301ZZZZ";
98+
expect(getSigningTypeFromPayload(nonHexPayload)).toEqual(SigningType.OPERATION);
99+
expect(decodeBeaconPayload(nonHexPayload, SigningType.OPERATION)).toEqual({
94100
error: "Cannot parse Beacon payload",
95-
result: "0501ZZZZ",
101+
result: nonHexPayload,
102+
});
103+
});
104+
105+
it("handles OPERATION payload", () => {
106+
const emptyPayload = "";
107+
expect(getSigningTypeFromPayload(emptyPayload)).toEqual(SigningType.RAW);
108+
expect(decodeBeaconPayload(emptyPayload, SigningType.RAW)).toEqual({
109+
result: "",
96110
});
97111
});
98112
});

packages/core/src/decodeBeaconPayload.ts

+31-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SigningType } from "@airgap/beacon-wallet";
22
import { CODEC, type ProtocolsHash, Uint8ArrayConsumer, getCodec } from "@taquito/local-forging";
3-
import { DefaultProtocol, unpackData } from "@taquito/michel-codec";
4-
import { hex2buf } from "@taquito/utils";
3+
import { DefaultProtocol, type MichelsonData, unpackData } from "@taquito/michel-codec";
4+
import { bytesToString, hex2buf } from "@taquito/utils";
55

66
import { CustomError } from "../../utils/src/ErrorContext";
77

@@ -25,14 +25,27 @@ export const decodeBeaconPayload = (
2525
switch (signingType) {
2626
case SigningType.MICHELINE:
2727
case SigningType.OPERATION: {
28-
const consumer = Uint8ArrayConsumer.fromHexString(payload);
29-
const uint8array = consumer.consume(consumer.length());
30-
const parsed = unpackData(uint8array);
28+
if (getSigningTypeFromPayload(payload) !== signingType) {
29+
throw new CustomError("Invalid prefix for signing type");
30+
}
3131

32-
if ("string" in parsed && Object.keys(parsed).length === 1) {
33-
result = parsed.string;
34-
} else {
35-
result = JSON.stringify(parsed);
32+
try {
33+
const consumer = Uint8ArrayConsumer.fromHexString(payload);
34+
const uint8array = consumer.consume(consumer.length());
35+
const parsed: MichelsonData = unpackData(uint8array);
36+
if ("string" in parsed && Object.keys(parsed).length === 1) {
37+
result = parsed.string;
38+
} else {
39+
result = JSON.stringify(parsed);
40+
}
41+
} catch {
42+
// "03" for operation
43+
// "05" + "01" + 8-padded-length for micheline
44+
const prefixLen = signingType === SigningType.MICHELINE ? 12 : 2;
45+
result = bytesToString(payload.slice(prefixLen));
46+
if (result.length === 0) {
47+
throw new CustomError("Invalid payload. Failed to decode.");
48+
}
3649
}
3750
break;
3851
}
@@ -54,7 +67,8 @@ export const decodeBeaconPayload = (
5467
}
5568

5669
return { result };
57-
} catch {
70+
} catch (error) {
71+
console.error(error);
5872
return { result: payload, error: "Cannot parse Beacon payload" };
5973
}
6074
};
@@ -63,3 +77,10 @@ const isValidASCII = (str: string) => str.split("").every(char => char.charCodeA
6377

6478
const parseOperationMicheline = (payload: string) =>
6579
getCodec(CODEC.MANAGER, DefaultProtocol as string as ProtocolsHash).decoder(payload);
80+
81+
export const getSigningTypeFromPayload = (payload: string): SigningType =>
82+
payload.startsWith("05")
83+
? SigningType.MICHELINE
84+
: payload.startsWith("03")
85+
? SigningType.OPERATION
86+
: SigningType.RAW;

0 commit comments

Comments
 (0)