What am I doing wrong?
Hi everyone!
I am doing a bootcamp project. The idea is to create an app in order to make transactions using ECDSA(elliptic curve algorithm). We use the js-ethereum-cryptography library.
What I have done:
- Generate a random private key, get its public key and its address. Store the random private key, its public key and wallet address in an object called personalKeys in Wallet.jsx.
- Invite the user to enter his wallet address in the app. Once entered, it looks for its corresponding public key in personalKeys so that the public key appears in the app.
- In my Transfer.jsx file, I create my sign(hashedMessage, privateKey) function. To get the private key, I use the wallet address entered by the user to look for its corresponding private key stored in the same personalKeys object.
- The user clicks on the “Sign Now” button to trigger the sign() function. The signature is displayed in the app if successfully signed.
- Once I get the signature, the address, amount, recipient, hashed message and signature are sent to the Server side.
- The server uses the hashed message and the signature to recover the public key and then its address. The server checks if the verified address is the same to the sender address.
My question:
Now, I am stuck on the last step (6): when I click on the TRANSFER button, I got this message: enter image description here
Could anyone help me? I’ve tested many times and tries different possibilities but still doesn’t work? Mybe there some problem between my Transfer.jsx and index.js files (client and server)? I would really appreciate your help! Thank you!
Below are my code snippet in different files:
Client-side:
-> App.jsx:
import Wallet from "./Wallet";
import Transfer from "./Transfer";
import "./App.scss";
import { useState } from "react";
function App() {
const [balance, setBalance] = useState(0);
const [address, setAddress] = useState("");
const [publicKey, setPublicKey] = useState("");
return (
<div className="app">
<Wallet
address={address}
setAddress={setAddress}
balance={balance}
setBalance={setBalance}
publicKey={publicKey}
setPublicKey={setPublicKey}
/>
<Transfer setBalance={setBalance} address={address} />
</div>
);
}
export default App;
-> Wallet.jsx:
import server from "./server";
//store all 3 of those in an object on the client side:
export const personalKeys = {
'private key': "fdee16480d7f616dd7c72bebe7c42d5954f7c799b084e9c7133929bf10b5430d",
'public key': "03f882882f75fbca3f8cc1efd3fffb1621dd50d68417b2d5b0ff35a04ba8b35471",
'wallet address': "9b862c8aed6d056c951eab8b192a36c27c149147"
};
//Use the address to search for the associated private key from the client side:
function addressToPublic(knownAddress) {
if (personalKeys['wallet address'] === knownAddress.toString()) {
const associatedPublicKey = personalKeys['public key'];
return associatedPublicKey;
} else {
setError("Address not valid.");
return null;
}
};
function Wallet({ address, setAddress, balance, setBalance, publicKey, setPublicKey }) {
async function onChange(evt) {
const address = evt.target.value;
setAddress(address);
const publicKey = addressToPublic(address);
setPublicKey(publicKey);
if (address) {
const {
data: { balance },
} = await server.get(`balance/${address}`);
setBalance(balance);
} else {
setBalance(0);
}
}
return (
<div className="container wallet">
<h1>Your Wallet</h1>
<label>
Wallet address:
<input placeholder="Type in your wallet address" value={address} onChange={onChange}></input>
</label>
<div>
Public key: {publicKey.slice(0, 10)}...
</div>
<div className="balance">Balance: {balance}</div>
</div>
);
}
export default Wallet;
-> Transfer.jsx:
import { useState } from "react";
import server from "./server";
import { personalKeys } from "./Wallet";
import { keccak256 } from "ethereum-cryptography/keccak";
import { utf8ToBytes } from "ethereum-cryptography/utils";
import { secp256k1 } from '@noble/curves/secp256k1';
// get the private key from Wallet:
function addressToPrivate(knownAddress) {
if (personalKeys['wallet address'] === knownAddress.toString()) {
const associatedPrivateKey = personalKeys['private key'];
return associatedPrivateKey;
} else {
setError("Address not valid.");
return null;
}
};
// get the hashed message:
function hashMessage(m) {
const hashedMessage = keccak256(utf8ToBytes(m));
return hashedMessage;
}
//get the signature:
function signMessage(msg, add) {
const signedMessage = secp256k1.sign(hashMessage(msg), addressToPrivate(add));
return signedMessage;
}
function Transfer({ address, setBalance }) {
const [sendAmount, setSendAmount] = useState("");
const [recipient, setRecipient] = useState("");
const [signature, setSignature] = useState(null);
const setValue = (setter) => (evt) => setter(evt.target.value);
const message = `I am sending ${sendAmount} ETH from ${address} to ${recipient}.`;
//function for the "sign now" button and get the signature:
function handleSignNow() {
const signature = signMessage(message, address);
setSignature(signature);
};
async function transfer(evt) {
evt.preventDefault();
try {
const {
data: { balance },
} = await server.post(`send`, {
sender: address,
amount: parseInt(sendAmount),
recipient,
hashedmsg: hashMessage(message),
sig: signature,
});
setBalance(balance);
} catch (ex) {
alert(ex.response.data.message);
}
}
return (
<form className="container transfer" onSubmit={transfer}>
<h1>Send Transaction</h1>
<label>
Send Amount
<input
placeholder="1, 2, 3..."
value={sendAmount}
onChange={setValue(setSendAmount)}
></input>
</label>
<label>
Recipient
<input
placeholder="Type an address, for example: 0x2"
value={recipient}
onChange={setValue(setRecipient)}
></input>
</label>
<button type="button" className="small-button" onClick={handleSignNow}>Sign Now</button>
{signature && (
<div>
Signature: {signature.toCompactHex()}
</div>
)}
<input type="submit" className="button" value="Transfer" />
</form>
);
}
export default Transfer;
Server-side:
index.js:
const express = require("express");
const app = express();
const cors = require("cors");
const port = 3042;
const { keccak256 } = require("ethereum-cryptography/keccak");
const { toHex } = require("ethereum-cryptography/utils");
app.use(cors());
app.use(express.json());
//also store the address + amounts on the server side:
const balances = {
"9b862c8aed6d056c951eab8b192a36c27c149147": 100,
"0x1": 10,
"0x2": 50,
};
app.get("/balance/:address", (req, res) => {
const { address } = req.params;
const balance = balances[address] || 0;
res.send({ balance });
});
app.post("/send", (req, res) => {
const { sender, recipient, amount, hashedmsg, sig } = req.body;
//get public key and address from signature:
const pubKey = sig.recoverPublicKey(hashedmsg);
const verifiedAddress = toHex(keccak256(pubKey.slice(1)).slice(-20));
if (sender !== verifiedAddress) {
res.status(400).send({ message: "Wrong signature!" });
return;
} else {
setInitialBalance(sender);
setInitialBalance(recipient);
}
if (balances[sender] < amount) {
res.status(400).send({ message: "Not enough funds!" });
} else {
balances[sender] -= amount;
balances[recipient] += amount;
res.send({ balance: balances[sender] });
}
});
app.listen(port, () => {
console.log(`Listening on port ${port}!`);
});
function setInitialBalance(address) {
if (!balances[address]) {
balances[address] = 0;
}
}
I've tried many changes and also to use toHex() and String() to format the type of values before transfer them from the front to the back. Everything works well till the Transfer button. The "sign up" button works too.