@@ -2,6 +2,26 @@ const _web3 = require('../utils/web3')
2
2
const dynamoDB = require ( '../utils/dynamo-db' )
3
3
const whitelist = require ( '../utils/whitelist' )
4
4
5
+ // Safe helpers
6
+ const ERC1271_ABI = [
7
+ { type : 'function' , name : 'isValidSignature' , stateMutability : 'view' ,
8
+ inputs : [
9
+ { name : '_hash' , type : 'bytes32' , internalType : 'bytes32' } ,
10
+ { name : '_signature' , type : 'bytes' , internalType : 'bytes' }
11
+ ] ,
12
+ outputs : [
13
+ { name : 'magicValue' , type : 'bytes4' , internalType : 'bytes4' }
14
+ ]
15
+ }
16
+ ]
17
+ const MAGIC_VALUE = '0x1626ba7e'
18
+ const DERIVED_ACCOUNT_KEY =
19
+ 'To keep your data safe and to use certain features of Kleros, we ask that you sign these messages to create a secret key for your account. This key is unrelated from your main Ethereum account and will not be able to send any transactions.'
20
+
21
+ const buildDigest = ( web3 , msg ) =>
22
+ web3 . utils . keccak256 ( "\x19Ethereum Signed Message:\n" + msg . length + msg )
23
+ //-----------------------------------------------------
24
+
5
25
module . exports . get = async ( event , _context , callback ) => {
6
26
// Initialize web3
7
27
const web3 = await _web3 ( )
@@ -56,21 +76,35 @@ module.exports.patch = async (event, _context, callback) => {
56
76
57
77
// Validate signature
58
78
const payload = JSON . parse ( event . body ) . payload
79
+ const isSafeLink = ! ! payload . isSafeLink
80
+
59
81
try {
60
- const account = await web3 . eth . accounts . recover (
61
- JSON . stringify ( payload . settings ) ,
62
- payload . signature
63
- )
64
- if (
65
- account !== payload . address &&
66
- account !==
67
- ( await dynamoDB . getItem ( {
68
- Key : { address : { S : payload . address } } ,
69
- TableName : 'user-settings' ,
70
- ProjectionExpression : 'derivedAccountAddress'
71
- } ) ) . Item . derivedAccountAddress . S
72
- )
73
- throw new Error ( 'Signature does not match supplied address.' )
82
+ if ( isSafeLink ) {
83
+ // One-time link call must be signed by the Safe itself
84
+ const valid = await new web3 . eth . Contract ( ERC1271_ABI , payload . address )
85
+ . methods . isValidSignature (
86
+ buildDigest ( web3 , DERIVED_ACCOUNT_KEY ) ,
87
+ payload . signature
88
+ )
89
+ . call ( )
90
+ if ( valid !== MAGIC_VALUE )
91
+ throw new Error ( 'Initial link must be signed by Safe.' )
92
+ } else {
93
+ const account = await web3 . eth . accounts . recover (
94
+ JSON . stringify ( payload . settings ) ,
95
+ payload . signature
96
+ )
97
+ if (
98
+ account !== payload . address &&
99
+ account !==
100
+ ( await dynamoDB . getItem ( {
101
+ Key : { address : { S : payload . address } } ,
102
+ TableName : 'user-settings' ,
103
+ ProjectionExpression : 'derivedAccountAddress'
104
+ } ) ) . Item . derivedAccountAddress . S
105
+ )
106
+ throw new Error ( 'Signature does not match supplied address.' )
107
+ }
74
108
} catch ( err ) {
75
109
console . error ( err )
76
110
return callback ( null , {
@@ -104,4 +138,4 @@ module.exports.patch = async (event, _context, callback) => {
104
138
}
105
139
} )
106
140
} )
107
- }
141
+ }
0 commit comments