Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Homogenize Bitcoin and Ethereum Examples #45

Merged
merged 3 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
"@ethereumjs/common": "^4.3.0",
"@ethereumjs/tx": "^5.3.0",
"@ethereumjs/util": "^9.0.3",
"@near-wallet-selector/bitte-wallet": "^8.9.13",
"@near-wallet-selector/core": "^8.9.13",
"@near-wallet-selector/here-wallet": "^8.9.13",
"@near-wallet-selector/meteor-wallet": "^8.9.13",
"@near-wallet-selector/modal-ui": "^8.9.13",
"@near-wallet-selector/my-near-wallet": "^8.9.13",
"@near-wallet-selector/bitte-wallet": "^8.9.13",
"@vitejs/plugin-react": "^4.2.1",
"axios": "^1.6.8",
"bech32": "^2.0.0",
"bitcoinjs-lib": "^6.1.5",
"bn.js": "^5.2.1",
"bs58check": "^3.0.1",
Expand Down
8 changes: 3 additions & 5 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import Navbar from "./components/Navbar"
import { Wallet } from "./services/near-wallet";
import { EthereumView } from "./components/Ethereum/Ethereum";
import { BitcoinView } from "./components/Bitcoin";

// CONSTANTS
const MPC_CONTRACT = 'v1.signer-prod.testnet';
import { MPC_CONTRACT } from './services/kdf/mpc';

// NEAR WALLET
const wallet = new Wallet({ network: 'testnet' });
Expand Down Expand Up @@ -47,8 +45,8 @@ function App() {
</select>
</div>

{chain === 'eth' && <EthereumView props={{ setStatus, MPC_CONTRACT, transactions }} />}
{chain === 'btc' && <BitcoinView props={{ setStatus, MPC_CONTRACT, transactions }} />}
{chain === 'eth' && <EthereumView props={{ setStatus, transactions }} />}
{chain === 'btc' && <BitcoinView props={{ setStatus, transactions }} />}
</div>
}

Expand Down
20 changes: 12 additions & 8 deletions src/components/Bitcoin.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { useState, useEffect, useContext } from "react";
import { NearContext } from "../context";

import { Bitcoin as Bitcoin } from "../services/bitcoin";
import { useDebounce } from "../hooks/debounce";
import PropTypes from 'prop-types';
import { Bitcoin } from "../services/bitcoin";

const BTC = Bitcoin;
const BTC = new Bitcoin('testnet');

export function BitcoinView({ props: { setStatus, MPC_CONTRACT, transactions } }) {
export function BitcoinView({ props: { setStatus, transactions } }) {
const { wallet, signedAccountId } = useContext(NearContext);

const [receiver, setReceiver] = useState("tb1q86ec0aszet5r3qt02j77f3dvxruk7tuqdlj0d5");
Expand All @@ -20,7 +20,7 @@ export function BitcoinView({ props: { setStatus, MPC_CONTRACT, transactions } }

const [derivation, setDerivation] = useState("bitcoin-1");
const derivationPath = useDebounce(derivation, 500);

const getSignedTx = async () => {
const signedTx = await wallet.getTransactionResult(transactions[0])
console.log('signedTx', signedTx)
Expand Down Expand Up @@ -53,9 +53,13 @@ export function BitcoinView({ props: { setStatus, MPC_CONTRACT, transactions } }

async function chainSignature() {
setStatus('🏗️ Creating transaction');

const { psbt, utxos } = await BTC.createTransaction({ from: senderAddress, to: receiver, amount, path: derivationPath, wallet });

setStatus('🕒 Asking MPC to sign the transaction, this might take a while...');

try {
const signedTransaction = await BTC.getSignature({ from: senderAddress, publicKey: senderPK, to: receiver, amount, path: derivationPath, wallet });
const signedTransaction = await BTC.requestSignatureToMPC({ psbt, utxos, publicKey: senderPK, path: derivationPath, wallet });
setStatus('✅ Signed payload ready to be relayed to the Bitcoin network');
setSignedTransaction(signedTransaction);
setStep('relay');
Expand All @@ -71,10 +75,10 @@ export function BitcoinView({ props: { setStatus, MPC_CONTRACT, transactions } }
setStatus('🔗 Relaying transaction to the Bitcoin network... this might take a while');

try {
const txHash = await BTC.broadcast({ from: senderAddress, publicKey: senderPK, to: receiver, amount, path: derivationPath, sig: signedTransaction });
const txHash = await BTC.broadcastTX(signedTransaction);
setStatus(
<>
<a href={`https://blockstream.info/testnet/tx/${txHash}`} target="_blank"> ✅ Successful </a>
<a href={`https://blockstream.info/testnet/tx/${txHash}`} target="_blank"> ✅ Successfully Broadcasted </a>
</>
);
} catch (e) {
Expand Down Expand Up @@ -125,6 +129,6 @@ export function BitcoinView({ props: { setStatus, MPC_CONTRACT, transactions } }
BitcoinView.propTypes = {
props: PropTypes.shape({
setStatus: PropTypes.func.isRequired,
MPC_CONTRACT: PropTypes.string.isRequired,
transactions: PropTypes.arrayOf(PropTypes.string).isRequired
}).isRequired
};
31 changes: 16 additions & 15 deletions src/components/Ethereum/Ethereum.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { useState, useEffect, useContext } from "react";
import { NearContext } from "../../context";

import { Ethereum } from "../../services/ethereum";
import { useDebounce } from "../../hooks/debounce";
import PropTypes from 'prop-types';
import { useRef } from "react";
import { TransferForm } from "./Transfer";
import { FunctionCallForm } from "./FunctionCall";
import { Ethereum } from "../../services/ethereum";
import { MPC_CONTRACT } from "../../services/kdf/mpc";

const Sepolia = 11155111;
const Eth = new Ethereum('https://rpc2.sepolia.org', Sepolia);
const Eth = new Ethereum('https://sepolia.drpc.org', Sepolia);

export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions } }) {
export function EthereumView({ props: { setStatus, transactions } }) {
const { wallet, signedAccountId } = useContext(NearContext);

const [loading, setLoading] = useState(false);
Expand All @@ -24,7 +25,7 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }
const [derivation, setDerivation] = useState(sessionStorage.getItem('derivation') || "ethereum-1");
const derivationPath = useDebounce(derivation, 1200);

const [reloaded, setReloaded] = useState(transactions.length? true : false);
const [reloaded, setReloaded] = useState(transactions.length ? true : false);

const childRef = useRef();

Expand All @@ -34,8 +35,8 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }

async function signTransaction() {
const { big_r, s, recovery_id } = await wallet.getTransactionResult(transactions[0]);
console.log({ big_r, s, recovery_id });
const signedTransaction = await Eth.reconstructSignatureFromLocalSession(big_r, s, recovery_id, senderAddress);
const signedTransaction = await Eth.reconstructSignedTXFromLocalSession(big_r, s, recovery_id, senderAddress);

setSignedTransaction(signedTransaction);
setStatus(`✅ Signed payload ready to be relayed to the Ethereum network`);
setStep('relay');
Expand All @@ -55,7 +56,7 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }

useEffect(() => {
setEthAddress()
console.log(derivationPath)

async function setEthAddress() {
const { address } = await Eth.deriveAddress(signedAccountId, derivationPath);
setSenderAddress(address);
Expand All @@ -69,13 +70,15 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }
async function chainSignature() {
setStatus('🏗️ Creating transaction');

const { transaction, payload } = await childRef.current.createPayload();
// const { transaction, payload } = await Eth.createPayload(senderAddress, receiver, amount, undefined);
const { transaction } = await childRef.current.createTransaction();

setStatus(`🕒 Asking ${MPC_CONTRACT} to sign the transaction, this might take a while`);
try {
const { big_r, s, recovery_id } = await Eth.requestSignatureToMPC(wallet, MPC_CONTRACT, derivationPath, payload, transaction, senderAddress);
const signedTransaction = await Eth.reconstructSignature(big_r, s, recovery_id, transaction, senderAddress);
// to reconstruct on reload
sessionStorage.setItem('derivation', derivationPath);

const { big_r, s, recovery_id } = await Eth.requestSignatureToMPC({ wallet, path: derivationPath, transaction });
const signedTransaction = await Eth.reconstructSignedTransaction(big_r, s, recovery_id, transaction);

setSignedTransaction(signedTransaction);
setStatus(`✅ Signed payload ready to be relayed to the Ethereum network`);
Expand All @@ -89,9 +92,8 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }
async function relayTransaction() {
setLoading(true);
setStatus('🔗 Relaying transaction to the Ethereum network... this might take a while');

try {
const txHash = await Eth.relayTransaction(signedTransaction);
const txHash = await Eth.broadcastTX(signedTransaction);
setStatus(
<>
<a href={`https://sepolia.etherscan.io/tx/${txHash}`} target="_blank"> ✅ Successful </a>
Expand Down Expand Up @@ -142,7 +144,7 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }
</>
)

function removeUrlParams () {
function removeUrlParams() {
const url = new URL(window.location.href);
url.searchParams.delete('transactionHashes');
window.history.replaceState({}, document.title, url);
Expand All @@ -152,7 +154,6 @@ export function EthereumView({ props: { setStatus, MPC_CONTRACT, transactions }
EthereumView.propTypes = {
props: PropTypes.shape({
setStatus: PropTypes.func.isRequired,
MPC_CONTRACT: PropTypes.string.isRequired,
transactions: PropTypes.arrayOf(PropTypes.string).isRequired
}).isRequired
};
12 changes: 5 additions & 7 deletions src/components/Ethereum/FunctionCall.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,13 @@ export const FunctionCallForm = forwardRef(({ props: { Eth, senderAddress, loadi
useEffect(() => { getNumber() }, []);

useImperativeHandle(ref, () => ({
async createPayload() {
async createTransaction() {
const data = Eth.createTransactionData(contract, abi, 'set', [number]);
const { transaction, payload } = await Eth.createPayload(senderAddress, contract, 0, data);
return { transaction, payload };
const { transaction } = await Eth.createTransaction({ sender: senderAddress, receiver: contract, amount: 0, data });
return { transaction };
},

async afterRelay() {
getNumber();
}
async afterRelay() { getNumber(); }
}));

return (
Expand Down Expand Up @@ -110,7 +108,7 @@ FunctionCallForm.propTypes = {
senderAddress: PropTypes.string.isRequired,
loading: PropTypes.bool.isRequired,
Eth: PropTypes.shape({
createPayload: PropTypes.func.isRequired,
createTransaction: PropTypes.func.isRequired,
createTransactionData: PropTypes.func.isRequired,
getContractViewFunction: PropTypes.func.isRequired,
}).isRequired,
Expand Down
10 changes: 5 additions & 5 deletions src/components/Ethereum/Transfer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { forwardRef } from "react";
import { useImperativeHandle } from "react";

export const TransferForm = forwardRef(({ props: { Eth, senderAddress, loading } }, ref) => {
const [receiver, setReceiver] = useState("0xe0f3B7e68151E9306727104973752A415c2bcbEb");
const [receiver, setReceiver] = useState("0xb8A6a4eb89b27703E90ED18fDa1101c7aa02930D");
const [amount, setAmount] = useState(0.005);

useImperativeHandle(ref, () => ({
async createPayload() {
const { transaction, payload } = await Eth.createPayload(senderAddress, receiver, amount, undefined);
return { transaction, payload };
async createTransaction() {
const { transaction } = await Eth.createTransaction({ sender: senderAddress, receiver, amount });
return { transaction };
},
async afterRelay() { }
}));
Expand Down Expand Up @@ -40,7 +40,7 @@ TransferForm.propTypes = {
senderAddress: PropTypes.string.isRequired,
loading: PropTypes.bool.isRequired,
Eth: PropTypes.shape({
createPayload: PropTypes.func.isRequired
createTransaction: PropTypes.func.isRequired
}).isRequired
}).isRequired
};
Expand Down
Loading