-
Notifications
You must be signed in to change notification settings - Fork 262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document better Safe message hash calculation #1555
Comments
Service does not accept a Do you think this PR adds enough info? #1573 |
Thanks @Uxio0 - I appreciate you updating the docs, this is super helpful. I'm still facing this issue when signing an EIP191 message and I feel like there might be something missing. You're saying EIP191 will be used. If that's the case, signing with const message = "Hello World";
const signature = await this.safeSigner.signMessage(message);
console.log(await this.safeSigner.getAddress()); // output: 0x4790.......... <-- this is the actual signer
const url = SAFE_TX_SERVICE_URLS[this.chainId] + '/v1/safes/' + this.safeAddress + '/messages/';
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message,
// safeAppId: 0,
signature,
}),
}); response.body: {
nonFieldErrors: [
'0xAc60AaFB573cBB6471e90237a0Dce07b21ead0c4 is not an owner of the Safe'
// that's not the address of the signer (see above) 0x4790..........
]
} I think it would be good to add some more info to the docs on how to sign/encode this data because according to EIP191 Thanks again for your help! |
Every message hash is different depending on the We must complete the documentation, I will reopen the issue |
Thank you @Uxio0 for sharing the link - this is really helpful. I've been trying to create a JS version of the However, when signing the result of this function with async getMessageHash(message: string): Promise<string> {
const SAFE_MESSAGE_TYPEHASH = ethers.getBytes(
"0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca"
);
const safeContract = new ethers.Contract(this.safeAddress, GnosisSafeL2Abi, this.ethAdapter as any);
const domainSeperator = await safeContract.domainSeparator();
console.log("domainSeperator: ", domainSeperator);
let messageBytes: Uint8Array;
if (typeof message === 'string') {
messageBytes = ethers.toUtf8Bytes(message);
} else {
messageBytes = message;
}
const messageHash = ethers.keccak256(messageBytes);
const safeMessageHash = ethers.keccak256(
ethers.solidityPacked(
["bytes32", "bytes32"],
[SAFE_MESSAGE_TYPEHASH, messageHash]
)
);
const finalHash = ethers.keccak256(
ethers.concat([
ethers.getBytes("0x19"),
ethers.getBytes("0x01"),
ethers.getBytes(domainSeperator),
ethers.getBytes(safeMessageHash),
])
);
return finalHash;
} |
If you need a JavaScript implementation, you can take a look at the tests on https://github.com/safe-global/safe-contracts/blob/main/test/core/Safe.Signatures.spec.ts |
Thanks @Uxio0, this is helpful and I'm able to sign transactions using this functionality. However, when I try to use the same logic for message signing, the signing fails ( In other words, transactions are working fine, signatures are not. I can't seem to find a way to send a valid signature to the service. It keeps coming back with
I'm using the logic of My apologies for keeping you busy with this, I'm clearly missing something. If you could point me to an implementation that successfully signs a message and then sends it to the transaction service endpoint |
Hi all, quick update on this. Finally got to the bottom of this issue. The API seems to throw the error when a trailing The correct URL is:
When adding a trailing
Video documenting the behavior: gnosis.safe.tx.service.signing.movThanks again for your help @Uxio0 - I think it would be great to add a better error message or somehow catch the trailing |
By using |
You're correct, didn't realize there was a redirect happening. Because of the GET redirect, it returns the signatures of the signed messages created via WalletConnect. That does not solve my problem. My problem remains the same, I'm not able to sign from an
@Uxio0 thanks for pointing this out. If you have any idea how to solve this in JavaScript, I would greatly appreciate your input. |
Sorry, I cannot think on more insight to give you. The examples on the Safe contracts repository are typescript, they are tested and working, you should be able to adapt them to work. Also our frontend does message handling so you can take a look at: https://github.com/safe-global/safe-wallet-web |
Adding messages doc here safe-global/safe-docs#235 |
The documentation was added here and also an example signing a message_hash calculated from Safe. |
What is needed?
The definition of
POST /api/v1/safes/<addr>/messages
in the Transaction Service API Definition doesn't specify the format of theMessage
variable. Looking at the python code, it looks like both, EIP712 objects as well as strings should be supported. However, when trying different formats, the API returns 400:'0x... is not an owner of the Safe'
A clear definition of the message format + encoding would be very helpful and should be added to the docs to avoid issues when signing messages.
Background
The following code should sign a message and send it to the tx service. The message is being sent but the transaction service returns an invalid owner, even though it is signed by one of the safe owners.
Response (400):
The message was signed by a Safe owner. However, the returned address
0x04a...
doesn't match the signer's address.If the same message + signature are run through
ethers.recoverAddress
, the resulting address is correct (the address of the safe owner):Additional Info
I tried both, EIP712 typed data as well as plaintext messages but it doesn't seem to be working. Every time I get another 400 with a random address:
'0x... is not an owner of the Safe'
.Is there a clear definition of the encoding of the
message
andsignature
variables or example code in JS?The text was updated successfully, but these errors were encountered: