Skip to content

Commit 360e024

Browse files
authored
Merge pull request #38 from kleros/feat/enable-safe-settings
feat(user-settings): Enable user settings for safes
2 parents f9cf992 + 8124634 commit 360e024

File tree

1 file changed

+49
-15
lines changed

1 file changed

+49
-15
lines changed

src/global/user-settings.js

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@ const _web3 = require('../utils/web3')
22
const dynamoDB = require('../utils/dynamo-db')
33
const 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+
525
module.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

Comments
 (0)