diff --git a/package.json b/package.json index 5cdc74d..f781e5c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "hash.js": "^1.1.7", "js-sha3": "^0.9.3", "keccak": "^3.0.4", + "multichain-tools": "4.0.0", "near-api-js": "^5.0.0", "prop-types": "^15.8.1", "react": "^18.2.0", @@ -52,4 +53,4 @@ "eslint-plugin-react-refresh": "^0.4.5", "vite": "^5.1.6" } -} \ No newline at end of file +} diff --git a/src/components/EVM/Ethereum.jsx b/src/components/EVM/Ethereum.jsx index e6b61f3..a792649 100644 --- a/src/components/EVM/Ethereum.jsx +++ b/src/components/EVM/Ethereum.jsx @@ -6,11 +6,20 @@ import { useDebounce } from "../../hooks/debounce"; import { getTransactionHashes } from "../../services/utils"; import { TransferForm } from "./Transfer"; import { FunctionCallForm } from "./FunctionCall"; -import { EthereumVM } from "../../services/evm"; +import { MPC_CONTRACT } from "../../services/kdf/mpc"; +import { EVM } from "multichain-tools"; +import { JsonRpcProvider } from "ethers"; +import Web3 from "web3"; -const Evm = new EthereumVM("https://sepolia.drpc.org"); +const web3 = new Web3("https://sepolia.drpc.org"); -const contractAddress = "0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3"; +const Evm = new EVM({ + providerUrl: "https://sepolia.drpc.org", + nearNetworkId: "testnet", + contract: MPC_CONTRACT, +}); + +const provider = new JsonRpcProvider("https://sepolia.drpc.org"); const transactions = getTransactionHashes(); @@ -19,7 +28,8 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { const [loading, setLoading] = useState(false); const [step, setStep] = useState(transactions ? "relay" : "request"); - const [signedTransaction, setSignedTransaction] = useState(null); + const [unsignedTransaction, setUnsignedTransaction] = useState(null); + const [signature, setSignature] = useState(null); const [senderLabel, setSenderLabel] = useState(""); const [senderAddress, setSenderAddress] = useState(""); const [balance, setBalance] = useState(""); // Add balance state @@ -39,10 +49,10 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { async function fetchEthereumGasPrice() { try { // Fetch gas price in Wei - const gasPriceInWei = await Evm.web3.eth.getGasPrice(); + const gasPriceInWei = await web3.eth.getGasPrice(); // Convert gas price from Wei to Gwei - const gasPriceInGwei = Evm.web3.utils.fromWei(gasPriceInWei, "gwei"); + const gasPriceInGwei = web3.utils.fromWei(gasPriceInWei, "gwei"); // Gas limit for a standard ETH transfer const gasLimit = 21000; @@ -76,17 +86,15 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { } async function signTransaction() { - const { big_r, s, recovery_id } = await wallet.getTransactionResult( - transactions[0] - ); - const signedTransaction = await Evm.reconstructSignedTXFromLocalSession( - big_r, - s, - recovery_id, - senderAddress - ); + const transaction = Evm.getTransaction("evm_transaction"); + + if (!transaction) return; + + setUnsignedTransaction(transaction); + + const signature = await wallet.getTransactionResult(transactions[0]); + setSignature(signature); - setSignedTransaction(signedTransaction); setStatus( "✅ Signed payload ready to be relayed to the Ethereum network" ); @@ -112,7 +120,7 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { }; const fetchEthereumAddress = async () => { - const { address } = await Evm.deriveAddress( + const { address } = await Evm.deriveAddressAndPublicKey( signedAccountId, derivationPath ); @@ -120,7 +128,8 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { setSenderLabel(address); if (!reloaded) { - const balance = await Evm.getBalance(address); + const balanceInWei = await provider.getBalance(address) + const balance = Web3.utils.fromWei(balanceInWei, 'ether'); setBalance(balance); // Update balance state } }; @@ -128,7 +137,11 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { async function chainSignature() { setStatus("🏗️ Creating transaction"); - const { transaction } = await childRef.current.createTransaction(); + const { transaction, mpcPayloads } = + await childRef.current.createTransaction(); + + Evm.setTransaction(transaction, "evm_transaction"); + setUnsignedTransaction(transaction); setStatus( `🕒 Asking ${MPC_CONTRACT} to sign the transaction, this might take a while` @@ -137,19 +150,24 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { // to reconstruct on reload sessionStorage.setItem("derivation", derivationPath); - const { big_r, s, recovery_id } = await Evm.requestSignatureToMPC({ - wallet, - path: derivationPath, - transaction, + // even tough ChainSignaturesContract.sign() is out there + // we can't use it here because FullAcess key pair can't be retrieved directly from WalletSelector + const signature = await wallet.callMethod({ + contractId: MPC_CONTRACT, + method: "sign", + args: { + request: { + payload: Array.from(mpcPayloads[0].payload), + path: derivationPath, + key_version: 0, + }, + }, + gas: "250000000000000", // 250 Tgas + deposit: 1, }); - const signedTransaction = await Evm.reconstructSignedTransaction( - big_r, - s, - recovery_id, - transaction - ); - setSignedTransaction(signedTransaction); + setSignature(signature); + setStatus( `✅ Signed payload ready to be relayed to the Ethereum network` ); @@ -167,7 +185,10 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { ); try { - const txHash = await Evm.broadcastTX(signedTransaction); + const txHash = await Evm.addSignatureAndBroadcast({ + transaction: unsignedTransaction, + mpcSignatures: [signature], + }); setStatus( <> @@ -262,7 +283,7 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT } }) { ) : ( )} diff --git a/src/components/EVM/Transfer.jsx b/src/components/EVM/Transfer.jsx index c56c984..80fded7 100644 --- a/src/components/EVM/Transfer.jsx +++ b/src/components/EVM/Transfer.jsx @@ -3,17 +3,26 @@ import { useState } from "react"; import PropTypes from "prop-types"; import { forwardRef } from "react"; import { useImperativeHandle } from "react"; +import Web3 from "web3"; -export const TransferForm = forwardRef(({ props: { Evm, senderAddress, loading } }, ref) => { - const [receiver, setReceiver] = useState("0xb8A6a4eb89b27703E90ED18fDa1101c7aa02930D"); - const [amount, setAmount] = useState(0.005); +export const TransferForm = forwardRef( + ({ props: { Evm, senderAddress, loading } }, ref) => { + const [receiver, setReceiver] = useState( + "0x427F9620Be0fe8Db2d840E2b6145D1CF2975bcaD" + ); + const [amount, setAmount] = useState("0.005"); - useImperativeHandle(ref, () => ({ - async createTransaction() { - return await Evm.createTransaction({ sender: senderAddress, receiver, amount }); - }, - async afterRelay() { } - })); + useImperativeHandle(ref, () => ({ + async createTransaction() { + return await Evm.getMPCPayloadAndTransaction({ + from: senderAddress, + to: receiver, + data: undefined, + value: Web3.utils.toWei(amount, "ether"), + }); + }, + async afterRelay() {}, + })); return ( <> @@ -42,7 +51,8 @@ export const TransferForm = forwardRef(({ props: { Evm, senderAddress, loading } className="form-control" value={amount} onChange={(e) => setAmount(e.target.value)} - step="0.01" + step="0.001" + min="0.001" disabled={loading} /> @@ -63,9 +73,9 @@ TransferForm.propTypes = { senderAddress: PropTypes.string.isRequired, loading: PropTypes.bool.isRequired, Evm: PropTypes.shape({ - createTransaction: PropTypes.func.isRequired - }).isRequired - }).isRequired + getMPCPayloadAndTransaction: PropTypes.func.isRequired, + }).isRequired, + }).isRequired, }; TransferForm.displayName = "TransferForm";