Skip to content

Commit

Permalink
Merge pull request #19 from near-examples/minor-fixes
Browse files Browse the repository at this point in the history
fix: multiple fixes
  • Loading branch information
gagdiez authored May 16, 2024
2 parents e389276 + 98726d4 commit ff2625c
Show file tree
Hide file tree
Showing 15 changed files with 142 additions and 4,297 deletions.
5 changes: 3 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/logo-black.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> <title>Vite + React</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<title>NEAR Multi-Chain</title>
</head>
<body>
<div id="root"></div>
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@
"@near-wallet-selector/my-near-wallet": "^8.9.5",
"axios": "^1.6.8",
"bitcoinjs-lib": "^6.1.5",
"bn.js": "^5.2.1",
"bs58check": "^3.0.1",
"elliptic": "^6.5.5",
"ethers": "^6.11.1",
"hash.js": "^1.1.7",
"keccak": "^3.0.4",
"near-api-js": "^3.0.4",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rxjs": "^7.8.1",
"vite-plugin-node-polyfills": "^0.21.0",
"web3": "^4.6.0"
},
"overrides": {
"near-api-js": "^3.0.4"
},
"devDependencies": {
"@types/react": "^18.2.64",
"@types/react-dom": "^18.2.21",
Expand All @@ -40,4 +46,4 @@
"eslint-plugin-react-refresh": "^0.4.5",
"vite": "^5.1.6"
}
}
}
File renamed without changes
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

27 changes: 11 additions & 16 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { NearContext } from './context';

import { useEffect, useState } from "react";
import Navbar from "./components/Navbar"
import { Wallet } from "./services/near-wallet";
Expand All @@ -11,29 +13,22 @@ const MPC_CONTRACT = 'multichain-testnet-2.testnet';
const wallet = new Wallet({ network: 'testnet', createAccessKeyFor: MPC_CONTRACT });

function App() {
const [isSignedIn, setIsSignedIn] = useState(false);
const [signedAccountId, setSignedAccountId] = useState('');
const [status, setStatus] = useState("Please login to request a signature");
const [chain, setChain] = useState('eth');

useEffect(() => {
const initFunction = async () => {
const isSignedIn = await wallet.startUp();
setIsSignedIn(isSignedIn);
}

initFunction();
}, []);
useEffect(() => { wallet.startUp(setSignedAccountId) }, []);

return (
<>
<Navbar wallet={wallet} isSignedIn={isSignedIn}></Navbar>
<NearContext.Provider value={{ wallet, signedAccountId }}>
<Navbar />
<div className="container">
<h4> 🔗 NEAR Multi Chain </h4>
<p className="small">
Safely control accounts on other chains through the NEAR MPC service. Learn more in the <a href="https://docs.near.org/abstraction/chain-signatures"> <b>documentation</b></a>.
</p>

{isSignedIn &&
{signedAccountId &&
<div style={{ width: '50%', minWidth: '400px' }}>

<div className="input-group input-group-sm mt-3 mb-3">
Expand All @@ -48,16 +43,16 @@ function App() {
</select>
</div>

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

<div className="mt-3 small text-center">
<span> {status} </span>
{status}
</div>
</div>
</>
</NearContext.Provider>
)
}

Expand Down
Binary file removed src/assets/favicon.ico
Binary file not shown.
1 change: 0 additions & 1 deletion src/assets/logo-white.svg

This file was deleted.

77 changes: 43 additions & 34 deletions src/components/Bitcoin.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { useState, useEffect, useContext } from "react";
import { NearContext } from "../context";

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

const BTC_NETWORK = 'testnet';
const BTC = new Bitcoin('https://blockstream.info/testnet/api', BTC_NETWORK);

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

const [receiver, setReceiver] = useState("tb1q86ec0aszet5r3qt02j77f3dvxruk7tuqdlj0d5");
const [amount, setAmount] = useState(1000);
const [loading, setLoading] = useState(false);
Expand All @@ -15,44 +19,56 @@ export function BitcoinView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
const [senderAddress, setSenderAddress] = useState("")
const [senderPK, setSenderPK] = useState("")

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

useEffect(() => { setBtcAddress(derivationPath) }, [derivationPath]);
useEffect(() => {
setSenderAddress('Waiting for you to stop typing...')
}, [derivation]);

async function setBtcAddress() {
setStatus('Querying your address and balance');
setSenderAddress('Deriving address...');
useEffect(() => {
setBtcAddress()

const { address, publicKey } = await BTC.deriveAddress(wallet.accountId, derivationPath);
const balance = await BTC.getBalance(address);
async function setBtcAddress() {
setStatus('Querying your address and balance');
setSenderAddress(`Deriving address from path ${derivationPath}...`);

setSenderAddress(address);
setSenderPK(publicKey);
setStatus(`Your Bitcoin address is: ${address}, balance: ${balance} satoshi`);
}
const { address, publicKey } = await BTC.deriveAddress(signedAccountId, derivationPath);
setSenderAddress(address);
setSenderPK(publicKey);

const balance = await BTC.getBalance(address);
setStatus(`Your Bitcoin address is: ${address}, balance: ${balance} satoshi`);
}
}, [signedAccountId, derivationPath]);

async function chainSignature() {
setStatus('🏗️ Creating transaction');
const payload = await BTC.createPayload(senderAddress, receiver, amount);

setStatus('🕒 Asking MPC to sign the transaction, this might take a while...');
const signedTransaction = await BTC.requestSignatureToMPC(wallet, MPC_CONTRACT, derivationPath, payload, senderPK);
console.log(signedTransaction)

setStatus('✅ Signed payload ready to be relayed to the Bitcoin network');

setSignedTransaction(signedTransaction);
setStep('relay');
setStatus('🕒 Asking MPC to sign the transaction, this might take a while...');
try{
const signedTransaction = await BTC.requestSignatureToMPC(wallet, MPC_CONTRACT, derivationPath, payload, senderPK);
setStatus('✅ Signed payload ready to be relayed to the Bitcoin network');
setSignedTransaction(signedTransaction);
setStep('relay');
} catch (e) {
setStatus(`❌ Error: ${e.message}`);
setLoading(false);
}
}

async function relayTransaction() {
setLoading(true);
setStatus('🔗 Relaying transaction to the Bitcoin network... this might take a while');

try {
const txHash = await BTC.relayTransaction(signedTransaction);
setStatus(`✅ Successful: https://blockstream.info/testnet/tx/${txHash}`);
setStatus(
<>
<a href={`https://blockstream.info/testnet/tx/${txHash}`}> ✅ Successful </a>
</>
);
} catch (e) {
setStatus(`❌ Error: ${e.message}`);
}
Expand All @@ -61,12 +77,6 @@ export function BitcoinView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
setLoading(false);
}

const handleDerivationChange = (event) => {
setStatus('Derivation path changed');
setSenderAddress('Waiting for you to stop typing...');
setDerivation(event.target.value);
}

const UIChainSignature = async () => {
setLoading(true);
await chainSignature();
Expand All @@ -76,22 +86,22 @@ export function BitcoinView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
return (
<>
<div className="row my-3">
<label className="col-sm-2 col-form-label col-form-label-sm">From:</label>
<label className="col-sm-2 col-form-label col-form-label-sm">Path:</label>
<div className="col-sm-10">
<input type="text" className="form-control form-control-sm" value={derivation} onChange={handleDerivationChange} disabled={loading}/>
<input type="text" className="form-control form-control-sm" value={derivation} onChange={(e) => setDerivation(e.target.value)} disabled={loading} />
<div className="form-text" id="eth-sender"> {senderAddress} </div>
</div>
</div>
<div className="row mb-3">
<label className="col-sm-2 col-form-label col-form-label-sm">To:</label>
<div className="col-sm-10">
<input type="text" className="form-control form-control-sm" value={receiver} onChange={(e) => setReceiver(e.target.value)} disabled={loading}/>
<input type="text" className="form-control form-control-sm" value={receiver} onChange={(e) => setReceiver(e.target.value)} disabled={loading} />
</div>
</div>
<div className="row mb-3">
<label className="col-sm-2 col-form-label col-form-label-sm">Amount:</label>
<div className="col-sm-10">
<input type="number" className="form-control form-control-sm" value={amount} onChange={(e) => setAmount(e.target.value)} step="1" disabled={loading}/>
<input type="number" className="form-control form-control-sm" value={amount} onChange={(e) => setAmount(e.target.value)} step="1" disabled={loading} />
<div className="form-text"> satoshi units </div>
</div>
</div>
Expand All @@ -107,7 +117,6 @@ export function BitcoinView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
BitcoinView.propTypes = {
props: PropTypes.shape({
setStatus: PropTypes.func.isRequired,
wallet: PropTypes.object.isRequired,
MPC_CONTRACT: PropTypes.string.isRequired,
}).isRequired
};
59 changes: 32 additions & 27 deletions src/components/Ethereum.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { useState, useEffect, useContext } from "react";
import { NearContext } from "../context";

import { Ethereum } from "../services/ethereum";
import { useEffect, useState } from "react";
import { useDebounce } from "../hooks/debounce";
import PropTypes from 'prop-types';

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

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

const [receiver, setReceiver] = useState("0xe0f3B7e68151E9306727104973752A415c2bcbEb");
const [amount, setAmount] = useState(0.01);
Expand All @@ -15,21 +18,27 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
const [signedTransaction, setSignedTransaction] = useState(null);
const [senderAddress, setSenderAddress] = useState("")

const [derivation, setDerivation] = useState("test");
const [derivation, setDerivation] = useState("ethereum-1");
const derivationPath = useDebounce(derivation, 1000);

useEffect(() => { setEthAddress(derivationPath) }, [derivationPath]);
useEffect(() => {
setSenderAddress('Waiting for you to stop typing...')
}, [derivation]);

async function setEthAddress() {
setStatus('Querying your address and balance');
setSenderAddress('Deriving address...');
useEffect(() => {
setEthAddress()

const { address } = await Eth.deriveAddress(wallet.accountId, derivationPath);
const balance = await Eth.getBalance(address);

setSenderAddress(address);
setStatus(`Your Ethereum address is: ${address}, balance: ${balance} ETH`);
}
async function setEthAddress() {
setStatus('Querying your address and balance');
setSenderAddress(`Deriving address from path ${derivationPath}...`);

const { address } = await Eth.deriveAddress(signedAccountId, derivationPath);
setSenderAddress(address);

const balance = await Eth.getBalance(address);
setStatus(`Your Ethereum address is: ${address}, balance: ${balance} ETH`);
}
}, [signedAccountId, derivationPath]);

async function chainSignature() {
setStatus('🏗️ Creating transaction');
Expand All @@ -41,7 +50,7 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
setSignedTransaction(signedTransaction);
setStatus(`✅ Signed payload ready to be relayed to the Ethereum network`);
setStep('relay');
} catch(e) {
} catch (e) {
setStatus(`❌ Error: ${e.message}`);
setLoading(false);
}
Expand All @@ -51,9 +60,12 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
setLoading(true);
setStatus('🔗 Relaying transaction to the Ethereum network... this might take a while');

try{
try {
const txHash = await Eth.relayTransaction(signedTransaction);
setStatus(`✅ Successful: https://sepolia.etherscan.io/tx/${txHash}`);
setStatus(<>
<a href={`https://sepolia.etherscan.io/tx/${txHash}`}> ✅ Successful </a>
</>
);
} catch (e) {
setStatus(`❌ Error: ${e.message}`);
}
Expand All @@ -62,12 +74,6 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
setLoading(false);
}

const handleDerivationChange = (event) => {
setStatus('Derivation path changed');
setSenderAddress('Waiting for you to stop typing...');
setDerivation(event.target.value);
}

const UIChainSignature = async () => {
setLoading(true);
await chainSignature();
Expand All @@ -77,22 +83,22 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
return (
<>
<div className="row mb-3">
<label className="col-sm-2 col-form-label col-form-label-sm">From:</label>
<label className="col-sm-2 col-form-label col-form-label-sm">Path:</label>
<div className="col-sm-10">
<input type="text" className="form-control form-control-sm" value={derivation} onChange={handleDerivationChange} disabled={loading}/>
<input type="text" className="form-control form-control-sm" value={derivation} onChange={(e) => setDerivation(e.target.value)} disabled={loading} />
<div className="form-text" id="eth-sender"> {senderAddress} </div>
</div>
</div>
<div className="row mb-3">
<label className="col-sm-2 col-form-label col-form-label-sm">To:</label>
<div className="col-sm-10">
<input type="text" className="form-control form-control-sm" value={receiver} onChange={(e) => setReceiver(e.target.value)} disabled={loading}/>
<input type="text" className="form-control form-control-sm" value={receiver} onChange={(e) => setReceiver(e.target.value)} disabled={loading} />
</div>
</div>
<div className="row mb-3">
<label className="col-sm-2 col-form-label col-form-label-sm">Amount:</label>
<div className="col-sm-10">
<input type="number" className="form-control form-control-sm" value={amount} onChange={(e) => setAmount(e.target.value)} step="0.01" disabled={loading}/>
<input type="number" className="form-control form-control-sm" value={amount} onChange={(e) => setAmount(e.target.value)} step="0.01" disabled={loading} />
<div className="form-text"> Ethereum units </div>
</div>
</div>
Expand All @@ -108,7 +114,6 @@ export function EthereumView({ props: { setStatus, wallet, MPC_CONTRACT } }) {
EthereumView.propTypes = {
props: PropTypes.shape({
setStatus: PropTypes.func.isRequired,
wallet: PropTypes.object.isRequired,
MPC_CONTRACT: PropTypes.string.isRequired,
}).isRequired
};
Loading

0 comments on commit ff2625c

Please sign in to comment.