How do I get the signature bytes from V, R, S?
Here is what I have tried:
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.16;
import {Test} from "forge-std/Test.sol";
import {console2} from "forge-std/console2.sol";
import "openzeppelin/utils/cryptography/ECDSA.sol";
library Signature {
error InvalidSignature();
function toVRS(bytes memory signature)
internal
pure
returns (
uint8 v,
bytes32 r,
bytes32 s
)
{
if (signature.length == 65) {
// ecrecover takes the signature parameters,
// and the only way to get them currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
} else {
revert InvalidSignature();
}
}
function fromVRS(
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (bytes memory) {
// v ++ (length(r) + 0x80 ) ++ r ++ (length(s) + 0x80) ++ s
// v ++ r ++ s
return abi.encodePacked(v, r, s);
}
}
contract SignTest is Test {
using ECDSA for bytes32;
uint256 private immutable alicePk = 1;
uint256 private immutable bobPk = 2;
address private immutable alice = vm.addr(1);
address private immutable bob = vm.addr(2);
function setUp() public {}
function testSignature() public {
bytes32 hash = keccak256("signed by Alice");
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, hash);
address signer = ecrecover(hash, v, r, s);
assertEq(alice, signer);
bytes memory signature = Signature.fromVRS(v, r, s);
(uint8 v1, bytes32 r1, bytes32 s1) = Signature.toVRS(signature);
signer = ecrecover(hash, v1, r1, s1);
assertEq(alice, signer);
}
function testRecoverVRS() public {
bytes32 h = keccak256(abi.encodePacked(uint256(1), "whatever"));
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePk, h);
bytes memory signature = Signature.fromVRS(v, r, s);
console2.log("signature length: %d", signature.length);
(uint8 v1, bytes32 r1, bytes32 s1) = Signature.toVRS(signature);
assertEq(v, v1);
assertEq(r, r1);
assertEq(s, s1);
}
}
But this doesn't work (tests fail).
Ideally, I'd love to keep the signature as v,r,s
, but I want bytes
for testing. I'm using foundry and I want to write a test for a function that takes bytes memory
signature (in solidity).
Is there a function v,r,s -> bytes
?
Best Answer
As @0xSanson mentioned in the comment above it should be
abi.encodePackaged(r, s, v)
. In the same order in which the data is read: