@@ -2,6 +2,26 @@ const _web3 = require('../utils/web3')
22const dynamoDB = require ( '../utils/dynamo-db' )
33const whitelist = require ( '../utils/whitelist' )
44
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+
525module . exports . get = async ( event , _context , callback ) => {
626 // Initialize web3
727 const web3 = await _web3 ( )
@@ -56,21 +76,35 @@ module.exports.patch = async (event, _context, callback) => {
5676
5777 // Validate signature
5878 const payload = JSON . parse ( event . body ) . payload
79+ const isSafeLink = ! ! payload . isSafeLink
80+
5981 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+ }
74108 } catch ( err ) {
75109 console . error ( err )
76110 return callback ( null , {
@@ -104,4 +138,4 @@ module.exports.patch = async (event, _context, callback) => {
104138 }
105139 } )
106140 } )
107- }
141+ }
0 commit comments