Skip to content

Commit d4cf813

Browse files
authored
fix: fix safe message (#3936)
* fix: fix safe message * fix: format
1 parent b9b7211 commit d4cf813

File tree

3 files changed

+38
-84
lines changed

3 files changed

+38
-84
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ next-env.d.ts
4141

4242
# .env
4343
.env
44+
45+
certificates

features/shared/jwt.ts

+35-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { SendTransactionsResponse } from '@safe-global/safe-apps-sdk'
1+
import type { SignMessageResponse } from '@safe-global/safe-apps-sdk'
22
import SafeAppsSDK from '@safe-global/safe-apps-sdk'
33
import { decode } from 'jsonwebtoken'
44
import type { Observable } from 'rxjs'
@@ -44,7 +44,7 @@ export function jwtAuthSetupToken$(
4444
}
4545

4646
interface GnosisSafeSignInDetails {
47-
safeTxHash: string
47+
messageHash: string
4848
challenge: string
4949
}
5050

@@ -72,15 +72,24 @@ async function getGnosisSafeDetails(
7272
}
7373

7474
const dataToSign = getDataToSignFromChallenge(newChallenge)
75-
const { safeTxHash } = (await sdk.txs.signMessage(dataToSign)) as SendTransactionsResponse
75+
const res = (await sdk.txs.signMessage(dataToSign)) as SignMessageResponse
76+
let messageHash: string | undefined
77+
if ('messageHash' in res) {
78+
messageHash = res.messageHash
79+
} else if ('safeTxHash' in res) {
80+
throw new Error('Please upgrade your SAFE')
81+
} else {
82+
throw new Error('Unexpected response type')
83+
}
84+
7685
localStorage.setItem(
7786
key,
7887
JSON.stringify({
79-
safeTxHash,
88+
messageHash,
8089
challenge: newChallenge,
8190
} as GnosisSafeSignInDetails),
8291
)
83-
return { challenge: newChallenge, safeTxHash, dataToSign }
92+
return { challenge: newChallenge, messageHash, dataToSign }
8493
}
8594

8695
async function requestJWT(
@@ -99,40 +108,35 @@ async function requestJWT(
99108

100109
const {
101110
challenge: gnosisSafeChallenge,
102-
safeTxHash,
111+
messageHash,
103112
dataToSign,
104113
} = await getGnosisSafeDetails(sdk, chainId, account, challenge)
105114

106115
// start polling
107116
const token = await new Promise<string | null>((resolve) => {
108117
let returnValue = (val: string | null) => resolve(val) // CAUTION: this function is reassigned later
109118
const interval = setInterval(async () => {
110-
try {
111-
const { detailedExecutionInfo } = await sdk.txs.getBySafeTxHash(safeTxHash)
112-
if (
113-
!(
114-
detailedExecutionInfo?.type === 'MULTISIG' &&
115-
detailedExecutionInfo.confirmations.length
116-
)
117-
) {
118-
throw new Error('GS: not ready')
119-
}
120-
121-
const isSigned = await sdk.safe.isMessageSigned(dataToSign)
122-
if (!isSigned) {
123-
throw new Error('Not signed yet')
119+
if (messageHash) {
120+
try {
121+
const isSigned = await sdk.safe.isMessageSigned(dataToSign)
122+
if (!isSigned) {
123+
throw new Error('Not signed yet')
124+
}
125+
const offchainSignature = await sdk.safe.getOffChainSignature(messageHash)
126+
if (!offchainSignature) {
127+
throw new Error('GS: not ready')
128+
}
129+
const safeJwt = await requestSignin({
130+
challenge: gnosisSafeChallenge,
131+
signature: offchainSignature,
132+
chainId,
133+
isGnosisSafe: true,
134+
}).toPromise()
135+
136+
return returnValue(safeJwt)
137+
} catch (error) {
138+
console.error('GS: error occurred', error)
124139
}
125-
126-
const safeJwt = await requestSignin({
127-
challenge: gnosisSafeChallenge,
128-
signature: safeTxHash,
129-
chainId,
130-
isGnosisSafe: true,
131-
}).toPromise()
132-
133-
return returnValue(safeJwt)
134-
} catch (error) {
135-
console.error('GS: error occurred', error)
136140
}
137141
}, 5 * 1000)
138142

handlers/signature-auth/signin.ts

+1-53
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { recoverPersonalSignature } from '@metamask/eth-sig-util'
2-
import { utils } from 'ethers'
32
import jwt from 'jsonwebtoken'
43
import type { NextApiHandler } from 'next'
54
import Web3 from 'web3'
@@ -64,26 +63,7 @@ export function makeSignIn(options: signInOptions): NextApiHandler {
6463
console.error('Check if argent wallet failed')
6564
}
6665

67-
if (body.isGnosisSafe) {
68-
try {
69-
const toHash = utils.defaultAbiCoder.encode(
70-
['bytes32', 'uint256'],
71-
[
72-
await getMessageHash(web3, utils.hashMessage(message), challenge.address),
73-
7 /* signedMessages slot */,
74-
],
75-
)
76-
const valueSlot = utils.keccak256(toHash).replace(/0x0/g, '0x')
77-
const slot = await web3.eth.getStorageAt(challenge.address, valueSlot as any)
78-
const [signed] = utils.defaultAbiCoder.decode(['uint256'], slot)
79-
80-
if (!signed.eq(1)) {
81-
throw new SignatureAuthError('Signature not correct')
82-
}
83-
} catch (e) {
84-
return res.status(400).json({ error: (e as Error).message })
85-
}
86-
} else if (isArgentWallet) {
66+
if (isArgentWallet) {
8767
if (!(await isValidSignature(web3, challenge.address, message, body.signature))) {
8868
throw new SignatureAuthError('Signature not correct')
8969
}
@@ -110,38 +90,6 @@ export function makeSignIn(options: signInOptions): NextApiHandler {
11090
}
11191
}
11292

113-
const GnosisSafeABI = [
114-
{
115-
name: 'domainSeparator',
116-
inputs: [],
117-
outputs: [
118-
{
119-
name: '',
120-
type: 'bytes32',
121-
},
122-
],
123-
stateMutability: 'view',
124-
type: 'function',
125-
},
126-
]
127-
128-
async function getMessageHash(web3: Web3, message: string, safe: string) {
129-
const SAFE_MSG_TYPESHASH = '0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca'
130-
const safeMessageHash = utils.keccak256(
131-
utils.defaultAbiCoder.encode(
132-
['bytes32', 'bytes32'],
133-
[SAFE_MSG_TYPESHASH, utils.keccak256(message)],
134-
),
135-
)
136-
137-
const contract = new web3.eth.Contract(GnosisSafeABI as any, safe)
138-
const domainSeparator = await contract.methods.domainSeparator().call()
139-
return utils.solidityKeccak256(
140-
['bytes1', 'bytes1', 'bytes32', 'bytes32'],
141-
[0x19, 0x01, domainSeparator, safeMessageHash],
142-
)
143-
}
144-
14593
export function recreateSignedMessage(challenge: ChallengeJWT): string {
14694
// This function needs to be in sync with frontend getDataToSignFromChallenge() function
14795
return `Sign to verify your wallet ${challenge.address} (${challenge.randomChallenge})`

0 commit comments

Comments
 (0)