-
Hello everyone, I'm using abstractionkit with React Native and Passkeys as signer. I'd love to be able to sign a message offchain with passkey to prove ownership of the address on Monerium with EIP-1271 I'm using this code const publicClient = createPublicClient({
chain: arbitrumSepolia,
transport: http(),
});
type SignMessageParams = {
message: string;
passkey: PasskeyStorageFormat;
smartAccount: SafeAccount;
};
export async function signMessage({ passkey, message, smartAccount }: SignMessageParams) {
const hash = hashMessage(message);
const abi = ["function getMessageHash(bytes memory message) public view returns (bytes32)"] as const;
const safeMessageHash = await publicClient.readContract({
address: smartAccount.accountAddress as Address,
abi: parseAbi(abi),
functionName: "getMessageHash",
args: [hash],
});
const assertion = await Passkey.get({
rpId: RP.id,
challenge: bufferToBase64URLString(toBytes(safeMessageHash)),
userVerification: "required",
allowCredentials: [{ id: passkey.id, type: "public-key" }],
});
if (!assertion) {
throw Error("ASSERTION_NULL");
}
const signatureBase64 = assertion.response.signature;
const signatureBuffer = Buffer.from(signatureBase64, "base64");
const webauthnSignatureData = {
authenticatorData: Buffer.from(assertion.response.authenticatorData, "base64").buffer,
clientDataFields: extractClientDataFields(assertion.response),
rs: extractSignature(signatureBuffer),
};
return SafeAccount.createWebAuthnSignature(webauthnSignatureData);
} To be used like this const passkey = {
id: RAW_ID,
coordinates: {x: X y: Y,}
};
const webauthPublicKey = passkey.coordinates;
const smartAccount = SafeAccount.initializeNewAccount([webauthPublicKey]);
const signature = await signMessage({
passkey,
smartAccount,
message: "I hereby declare that I am the address owner.",
}); But I'm not able to generate a valid signature. Monerium throw I also tried to verify it using Candide built in method but it's also failing const verify = await SafeAccount.verifyWebAuthnSignatureForMessageHash(
config.ARBITRUM_SEPOLIA.rpc,
passkey.coordinates,
hashMessage("I hereby declare that I am the address owner."),
signature,
); Any help would be much appreciated ! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
Hello @DavoCg thanks for opening this discussion. I have just created an example that signs and verifies a safe message using Passkeys. I noticed you are using viem and not ethers, so if you are still having trouble, let me know and I can share a version of te same example using viem. Let us know how it goes https://github.com/Sednaoui/safe-passkeys-sign-and-verify-message |
Beta Was this translation helpful? Give feedback.
-
Thanks @Sednaoui, the last commit you made works great. I was able to have a valid 1271 sig type SignMessageParams = {
message: string;
passkey: PasskeyStorageFormat;
smartAccount: SafeAccount;
};
export async function signMessage({ passkey, message, smartAccount }: SignMessageParams) {
const hash = hashMessage(message);
const chainId = 421614n;
const safeMessageHash = await getMessageHashForSafe(smartAccount.accountAddress, hash, chainId);
const assertion = await Passkey.get({
rpId: RP.id,
challenge: bufferToBase64URLString(toBytes(safeMessageHash)),
userVerification: "required",
allowCredentials: [{ id: passkey.id, type: "public-key" }],
});
if (!assertion) {
throw Error("ASSERTION_NULL");
}
const signatureBase64 = assertion.response.signature;
const signatureBuffer = Buffer.from(signatureBase64, "base64");
const webauthnSignatureData = {
authenticatorData: Buffer.from(assertion.response.authenticatorData, "base64").buffer,
clientDataFields: extractClientDataFields(assertion.response),
rs: extractSignature(signatureBuffer),
};
const webAuthnSignature = SafeAccount.createWebAuthnSignature(webauthnSignatureData);
const signerSignaturePair: SignerSignaturePair = {
signer: passkey.coordinates,
signature: webAuthnSignature,
};
return SafeAccount.buildSignaturesFromSingerSignaturePairs([signerSignaturePair], { isInit: false });
}
function getMessageHashForSafe(safeAccountAddress: `0x${string}`, message: string, chainId: bigint): `0x${string}` {
const SAFE_MSG_TYPEHASH = "0x60b3cbf8b4a223d68d641b3b6ddf9a298e7f33710cf3d3a9d1146b5a6150fbca";
const DOMAIN_SEPARATOR_TYPEHASH = "0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218";
const domainSeparator = keccak256(
encodeAbiParameters(
[{ type: "bytes32" }, { type: "uint256" }, { type: "address" }],
[DOMAIN_SEPARATOR_TYPEHASH, chainId, safeAccountAddress],
),
);
const encodedMessage = encodeAbiParameters(
[{ type: "bytes32" }, { type: "bytes32" }],
[SAFE_MSG_TYPEHASH, keccak256(toBytes(message))],
);
return keccak256(concat(["0x19", "0x01", domainSeparator, keccak256(encodedMessage)]));
} |
Beta Was this translation helpful? Give feedback.
Hello @DavoCg thanks for opening this discussion. I have just created an example that signs and verifies a safe message using Passkeys. I noticed you are using viem and not ethers, so if you are still having trouble, let me know and I can share a version of te same example using viem.
Let us know how it goes
https://github.com/Sednaoui/safe-passkeys-sign-and-verify-message