I'm trying to verify a signature created with web3js in Solidity, but do not get the correct signer.
In web3js:
var address = '0x5B38Da6a701c568545dCfcB03FcB875f56beddC4'; // == msg.sender
var signature = web3.eth.accounts.sign(address, PRIVATE_KEY);
// check:
var signer = web3.eth.accounts.recover(signature.message, signature.signature);
console.log(signer); // == PUBLIC_KEY
In Solidity:
function verify(bytes memory signature) public view returns (address) {
bytes32 hash = keccak256(abi.encodePacked(msg.sender));
bytes32 messageHash = hash.toEthSignedMessageHash();
address signer = messageHash.recover(signature);
return signer; // != PUBLIC_KEY
}
signer in Solidity has different value than PUBLIC_KEY in web3js.
Tested by creating simple contract in Remix and calling verify() with signature from web3js.
Quick check I did was that
web3.utils.keccak256(address)
has same value as
keccak256(abi.encodePacked(msg.sender))
so address (= message signed) in web3js is same as msg.sender after hashing.
Related
The smart contract I have written seems to have an error where the gas fees are unpredictable and even if I increase the max limit, the transaction fails and gets reverted. This specific function uses a bool and am wondering if it is the cause.
I was trying to pass the function goodsDelivered with the exchangeID which is the transaction hash from executing function openExchange, however when I try changing the goodsDelivered to 'true', I get a warning from metamask stating: unpredictable_gas_limit and gets reverted. Initially I thought it was an error from the client side where I wrote js code to be able to interact with the smart contract but when I tried writing on etherscan directly there was also the transaction failed. I have also tried debugging on Remix but it functions fine on remix.
pragma solidity ^0.8.0;
contract Exchange{
struct exchange{
uint amount;
address buyer;
address seller;
address agent;
bool goodsDelivered;
}
constructor() {
}
uint nonce;
mapping (bytes32 => exchange) public exchangeRegistry;
function openExchange(address _seller, address _agent) external payable returns (bytes32 _exchangeId) {
address _buyer = msg.sender;
uint _amount = msg.value;
_exchangeId = keccak256(abi.encodePacked(_buyer, nonce));
exchangeRegistry[_exchangeId].amount = _amount;
exchangeRegistry[_exchangeId].buyer = _buyer;
exchangeRegistry[_exchangeId].seller = _seller;
exchangeRegistry[_exchangeId].agent = _agent;
nonce += 1;
}
function withdrawAmount(bytes32 _exchangeId) external payable returns (bool){
require((exchangeRegistry[_exchangeId].goodsDelivered == true && msg.sender == exchangeRegistry[_exchangeId].seller)
|| (exchangeRegistry[_exchangeId].goodsDelivered == false && msg.sender == exchangeRegistry[_exchangeId].buyer));
payable(msg.sender).transfer(exchangeRegistry[_exchangeId].amount);
return (true);
}
function goodsDelivered(bytes32 _exchangeId, bool _goodsDelivered) external returns (bool){
require(msg.sender == exchangeRegistry[_exchangeId].agent);
exchangeRegistry[_exchangeId].goodsDelivered = _goodsDelivered;
}
}
CONTRACT_REVERT_EXECUTED
Not sure what I'm doing wrong but I'm trying to call a function and it takes in one parameter and I made sure it was correct but it still reverts. This is hedera-hashgraph using HederaTokenService.
Smart Contract:
pragma solidity ^0.8.11;
import "./hip-206/HederaTokenService.sol";
import "./hip-206/HederaResponseCodes.sol";
contract Minting is HederaTokenService {
address tokenAddress;
bytes metadata;
string baseURI = "abc";
uint64 mintPrice;
function mintNonFungibleToken(uint64 _amount) external payable {
bytes[] memory nftMetadatas = generateBytesArrayForHTS(
baseURI,
_amount
);
(
int256 response,
uint64 newTotalSupply,
) = HederaTokenService.mintToken(tokenAddress, _amount, metadata);
if (response != HederaResponseCodes.SUCCESS) {
revert("Mint Failed");
}
}
// #dev Helper function which generates array of addresses required for HTSPrecompiled
function generateAddressArrayForHTS(address _address, uint256 _items)
internal
pure
returns (address[] memory _addresses)
{
_addresses = new address[](_items);
for (uint256 i = 0; i < _items; i++) {
_addresses[i] = _address;
}
}
// #dev Helper function which generates array required for metadata by HTSPrecompiled
function generateBytesArrayForHTS(bytes memory _bytes, uint256 _items)
internal
pure
returns (bytes[] memory _bytesArray)
{
_bytesArray = new bytes[](_items);
for (uint256 i = 0; i < _items; i++) {
_bytesArray[i] = _bytes;
}
}
Calling the transaction in js:
const contractMint = await new ContractExecuteTransaction()
.setContractId(contractId)
.setGas(3000000)
.setFunction(
"mintFungibleToken",
new ContractFunctionParameters().addUint64(1)
)
.setMaxTransactionFee(new Hbar(2));
Also note that a REVERT does usually contain useful information, you can navigate to hashscan.io to look up the response from your smart contract, for example
https://hashscan.io/testnet/transaction/1675427464.278782297?tid=0.0.1189-1675427451-309271560
shows a contract that reverted, the error message is 0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b52656d697420746f6b656e20616c726561647920637265617465640000000000
with the error message starting with 0x08c379a, we know it's a string, so we can decode it
Hacky way:
Navigate to: https://www.rapidtables.com/convert/number/hex-to-ascii.html
Paste the above (remove the 0x)
Choose ASCII for character encoding
Press convert
Output: Ãy Remit token already created
In Javascript
const {ethers} = require("ethers");
async function main() {
const error = "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b52656d697420746f6b656e20616c726561647920637265617465640000000000";
const reason = ethers.utils.defaultAbiCoder.decode(
['string'],
ethers.utils.hexDataSlice(error, 4)
)
console.log(reason);
};
void main();
It looks like you're trying to call mintFungibleToken from JS, but the smart contract doesn't have a mintFungibleToken function; the smart contract function name is mintNonFungibleToken.
In the following claimPayment function that is used to claim a payment made earlier to this contract, the line bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); has this as part of the signed message. This makes me wonder what is this and what the type of this is. If I'm returning this in a function, what type is used for it? Thanks.
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]);
usedNonces[nonce] = true;
// this recreates the message that was signed on the client
bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));
require(recoverSigner(message, signature) == owner);
payable(msg.sender).transfer(amount);
}
this is a pointer to the current class instance, as in many other programming languages. You can for example point to public methods:
pragma solidity ^0.8.0;
contract MyContract {
function foo() external {
this.bar();
}
function bar() public {
}
}
When this is typecasted, it takes a form of the address, where the current instance is deployed.
pragma solidity ^0.8.0;
contract MyContract {
function foo() external view returns (bytes memory) {
return abi.encodePacked(this);
}
function bar() external view returns (address) {
return address(this);
}
}
I have deployed smart contract using remix IDE, launched with Injected Web3 on Ropsten test network. I could call BuyTokens function within solidity IDE successfully, but when tried to buy tokens with metamask from other address transaction get reverted. I can see the difference between those operations on ropsten.etherscan explorer - the difference is in Input Data field.
Metamask transaction has value 0x and transaction via remix is:
Function: buyTokens() ***
MethodID: 0xd0febe4c
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Token {
// Track how many tokens are owned by each address.
mapping (address => uint256) public balanceOf;
// Modify this section
string public name = "DemoCoin";
string public symbol = "DC";
uint8 public decimals = 8;
uint256 public totalSupply = 1000000000 * (uint256(10) ** decimals);
address public owner;
//uint scaler = 10e18; // == 1 ETH in wei
//uint public coinPrice = 20; //initial price => 20 cents
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
// Initially assign all tokens to the contract's creator.
owner = msg.sender;
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
// Might be executed automaticlly
// https://blog.chronologic.network/schedule-your-transaction-now-using-mycrypto-and-myetherwallet-17b48166b412
// function changeCoinPrice() public {
// uint newCoinPrice;
// require(msg.sender == address(0));
// coinPrice = newCoinPrice;
// }
function buyTokens() public payable {
// msg.value in wei so 1ETH = 10e18
// lets set 0.20 cents for 1 token
uint paidAmount;
require(balanceOf[msg.sender] >= paidAmount);
require(balanceOf[owner] >= value);
uint tokens;
tokens = value/10e14;
balanceOf[owner] -= tokens;
balanceOf[msg.sender] += tokens;
emit Transfer(owner, msg.sender, tokens);
}
function msgSenderBalancce() public view returns (uint) {
return balanceOf[msg.sender];
}
function withDrawEth() public view {
require(msg.sender == owner);
}
}
Why these methods are called diffrently? And how to add method id in metamask? Or am I missing something and this should be handled in other way?
MetaMask has a very basic UI. It only allows transfers of ETH and standardized tokens, but it doesn't show any buttons to call other contract functions. It also doesn't allow creating any custom buttons in their UI.
You'll need to set the data field of the transaction to 0xd0febe4c (which effectively executes the buyTokens() function).
But - they also don't allow specifying the data field value manually in the UI, so you'll need to preset it using the Ethereum provider API.
Your web app connects to the user's MetaMask acccount. It opens a MetaMask window and the user needs to manually confirm the connect.
The web app sends a request to MetaMask specifying the transaction with data field value.
The user confirms the transaction (which now includes the data field value 0xd0febe4c) in their MetaMask UI.
The code block of getting signer address via ecrecover in solidity contract code is working well with Ethereum but in TRON it is returning wrong address
My contract side code is
function validate(string memory strTest,uint8 v, bytes32 r, bytes32 s) public view returns(address){
bytes32 prefixedHash = keccak256(strTest);
return ecrecover(keccak256(prefixedHash),v,r,s);
// return ecrecover(prefixedHash,v,r,s):
}
and dapp side code is
msg = tronWeb.sha3("this is test string");
var signature = await tronWeb.trx.sign(msg);
var r=signature.substr(0,66);
var s= "0x" + signature.substr(66,64);
var v="0x" + signature.substr(signature.length-2);
retValue = await thisContractInstance.validate("this is test string",v,r,s).call();
but in both cases ( one case is commented in contract side code) getting wrong signer address in TRON shasta network
Are you trying to sign a string or sign transaction? #ashwin
I had the same kind of issues signing a string, and the below helped me out.
Tronweb uses different methods for signing transactions and signing strings.
1) For transactions you can refer this: https://github.com/TRON-US/tronweb/blob/master/src/lib/trx.js#L651,
2) Regarding signing string, it uses
ethers.utils.SigningKey.signDigest ()
of Ethereum: https://docs.ethers.io/ethers.js/html/api-advanced.html?highlight=signingkey#id8, note that the final v byte will always be nomalized for Solidity (either 27 or 28)
If you are trying to sign the msg as mentioned here,
var signature = await tronWeb.trx.sign(msg);
you can follow the second.
You could refer the article https://medium.com/#coredevs/verifying-elliptic-curve-digital-signature-with-tron-smart-contract-5d11347e7b5b.
This will be useful to accomplish signing the strings,
along with the above answer provided by #Aldo.
Happy coding.
Taken from here:
The smart contract code:
contract Verifier {
function recoverAddr(bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) returns (address) {
return ecrecover(msgHash, v, r, s);
}
function isSigned(address _addr, bytes32 msgHash, uint8 v, bytes32 r, bytes32 s) returns (bool) {
return ecrecover(msgHash, v, r, s) == _addr;
}
}
The client code:
const ethers = tronWeb.utils.ethersUtils;
let contract = await tronWeb.contract().at(contract_address);
let signingKey = new ethers.SigningKey(tronWeb.defaultPrivateKey);
let message = "This is some message";
let messageBytes = ethers.toUtf8Bytes(message);
let messageDigest = ethers.keccak256(messageBytes);
let signature = signingKey.signDigest(messageDigest);
let hexAddress = await contract.recoverAddr(messageDigest, signature.v, signature.r, signature.s).call();
According to https://github.com/tronprotocol/tronweb/blob/1435436f83b910f19e9205998e348ea093732ce5/src/lib/trx.js#L629
tronWeb.trx.sign() does the following
Pass in 32 bytes of hash
The prefix "\x19TRON Signed Message:\n32" needs to be added first
Generate a new hash with the keccak256 algorithm
Signature (TronLink or private key)
Therefore, when using solidity's ecrecover, the hash of step 3 is passed in.
function getHash(hash) {
hash = hash.replace(/^0x/, '')
if (!tronWeb.utils.isHex(hash)) {
return
}
if (hash.length !== 64) {
return
}
const {
keccak256,
toUtf8Bytes
} = tronWeb.utils.ethersUtils
const hashArray = tronWeb.utils.code.hexStr2byteArray(hash)
const TRX_MESSAGE_PREFIX = toUtf8Bytes('\x19TRON Signed Message:\n32')
return keccak256([
...TRX_MESSAGE_PREFIX,
...hashArray
])
}