You're on the right track! To calculate v
, you can adapt this logic :
Determining the v
Value:
The ECDSA signature used in Ethereum consists of three key elements: r
, s
, and a recovery identifier named v
. Historically, before the implementation of EIP-155, the value of v
could be either 27 or 28. However, since the adoption of EIP-155, the value of v
is now determined based on the chain_id
of the network where the transaction is being executed, thus altering the method of calculating this essential component of the signature.
For transactions on networks where EIP-155 is active, the value of v
is calculated using the formula v = chain_id * 2 + 35
or v = chain_id * 2 + 36
.
To identify the correct value of v
in an Ethereum signature, one can use the function ecrecover(sig, v, r, s)
. This function returns the public key corresponding to an Ethereum signature. Since you have already calculated the Ethereum address before, we know what the result of this equation should be. Thus, it is possible to test the two potential values of v
to determine which one is correct.
public static calculateV(address: Buffer, digest: Buffer, r: Buffer, s: Buffer, chainId?: bigint) : bigint {
/**
* This is the function to find the right v value
* There are two matching signatues on the elliptic curve
* we need to find the one that matches to our public key
* it can be v = `candidate_1` or v = `candidate_2`
*/
const candidate_1 = (chainId) ? (chainId * BigInt(2) + BigInt(35)) : BigInt(27);
const candidate_2 = (chainId) ? (chainId * BigInt(2) + BigInt(36)) : BigInt(28);
if (Buffer.compare(address, ethutil.publicToAddress(ethutil.ecrecover(digest, candidate_1, r, s, chainId)) === 0) {
return candidate_1;
} else if (Buffer.compare(address, ethutil.publicToAddress(ethutil.ecrecover(digest, candidate_2, r, s, chainId)) === 0) {
return candidate_2;
} else {
return BigInt(-1);
}
}
=> For more details, you can read this article, everything is explained there : https://jonathanokz.medium.com/secure-an-ethereum-wallet-with-a-kms-provider-2914bd1e4341
Alternatively, you can use this npm library to handle all the complexity for you : https://github.com/JonathanOkz/web3-kms-signer
import { Signer } from "@web3-kms-signer/core";
import { KMSWallets } from "@web3-kms-signer/kms-wallets";
import { KMSProviderAWS } from "@web3-kms-signer/kms-provider-aws";
const awsConfig = {
region: 'us-east-1',
credentials: {
accessKeyId: 'YOUR_AWS_ACCESS_KEY_ID',
secretAccessKey: 'YOUR_AWS_SECRET_ACCESS_KEY'
}
};
const provider = new KMSProviderAWS(awsConfig);
const chainId = 3; // Ropsten
const signer = new Signer(new KMSWallets(provider), chainId);
const txData = {
nonce: '0x00', // Replace with actual nonce
gasPrice: '0x09184e72a000', // Replace with actual gas price
gasLimit: '0x2710', // Replace with actual gas limit
to: '0x0000000000000000000000000000000000000000', // Replace with recipient address
value: '0x00', // Amount to send
data: '0x0', // Data payload if any
};
const signedTx = await signer.signTransaction({ keyId: 'keyId' }, txData);