-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #29 from PiVortex/add-NFTs-to-seperate-file
Restructure and add nft-chain-keys
- Loading branch information
Showing
39 changed files
with
1,011 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,16 @@ | ||
# NEAR Multichain Examples w/ Chain Signatures | ||
|
||
![alpha badge](https://img.shields.io/badge/status-alpha-red) | ||
[![PRs welcome](https://img.shields.io/badge/PRs-welcome-green)](https://github.com/near-examples/near-multichain/pulls) | ||
[![Chain Signatures Docs](https://img.shields.io/badge/Chain_Signatures_Docs-blue)](https://near-docs.onrender.com/concepts/abstraction/chain-signatures) | ||
[![Dev Support](https://img.shields.io/badge/DEV_SUPPORT-red)](https://t.me/neardev) | ||
|
||
An example on signing and executing transactions across multiple blockchain protocols from one NEAR account | ||
|
||
> Currently only available on **testnet**, and should be used to control only `testnet` accounts on other chains | ||
--- | ||
|
||
## Requirements | ||
|
||
- `npm` or `yarn` | ||
- NEAR `testnet` account using [MyNEARWallet](https://mynearwallet.com/) | ||
|
||
## Installation | ||
|
||
```bash | ||
npm install # or yarn | ||
npm run dev # or yarn dev | ||
``` | ||
|
||
> [!CAUTION] | ||
> Chain Signatures are currently in `alpha` and should only be used in a `testnet` environment. | ||
# NEAR Multichain Examples w/ Chain Signatures | ||
|
||
![alpha badge](https://img.shields.io/badge/status-alpha-red) | ||
[![PRs welcome](https://img.shields.io/badge/PRs-welcome-green)](https://github.com/near-examples/near-multichain/pulls) | ||
[![Chain Signatures Docs](https://img.shields.io/badge/Chain_Signatures_Docs-blue)](https://docs.near.org/concepts/abstraction/chain-signatures) | ||
[![Dev Support](https://img.shields.io/badge/DEV_SUPPORT-red)](https://t.me/neardev) | ||
|
||
This repo contains multiple examples of signing and executing transactions across multiple blockchain protocols from a NEAR account | ||
|
||
- [standard-multichain](./standard-multichain/): send transactions to foreign chains from a NEAR account. | ||
- [nft-chain-keys](./nft-chain-keys/): send transactions to Ethereum through transferable NFT Chain Keys. | ||
|
||
> Currently only available on **testnet**, and should be used to control only `testnet` accounts on other chains | ||
> [!CAUTION] | ||
> Chain Signatures are currently in `alpha` and should only be used in a `testnet` environment. |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# nft-chain-keys | ||
|
||
An example of signing and executing transactions for Ethereum from a NEAR account through NFT Chain Keys. This enables Ethereum accounts to be swapped via NFT standards on NEAR. | ||
|
||
> Currently only available on **testnet**, and should be used to control only `testnet` accounts on other chains | ||
--- | ||
|
||
## Requirements | ||
|
||
- `npm` or `yarn` | ||
- NEAR `testnet` account using [MyNEARWallet](https://testnet.mynearwallet.com/) or [Meteor Wallet](https://wallet.meteorwallet.app/wallet) | ||
|
||
## Installation | ||
|
||
```bash | ||
npm install # or yarn | ||
npm run dev # or yarn dev | ||
``` | ||
|
||
--- | ||
|
||
For a detailed explanation of how this example works, please refer to the [documentation](https://docs.near.org/build/chain-abstraction/nft-chain-keys). | ||
|
||
> [!CAUTION] | ||
> Chain Signatures are currently in `alpha` and should only be used in a `testnet` environment. |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"name": "new", | ||
"private": true, | ||
"version": "0.0.0", | ||
"type": "module", | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "vite build", | ||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"@ethereumjs/common": "^4.3.0", | ||
"@ethereumjs/tx": "^5.3.0", | ||
"@ethereumjs/util": "^9.0.3", | ||
"@near-wallet-selector/core": "^8.9.5", | ||
"@near-wallet-selector/meteor-wallet": "^8.9.5", | ||
"@near-wallet-selector/modal-ui": "^8.9.5", | ||
"@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", | ||
"@vitejs/plugin-react": "^4.2.1", | ||
"eslint": "^8.57.0", | ||
"eslint-plugin-react": "^7.34.0", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-refresh": "^0.4.5", | ||
"vite": "^5.1.6" | ||
} | ||
} |
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { NearContext } from './context'; | ||
|
||
import { useEffect, useState } from "react"; | ||
import Navbar from "./components/Navbar" | ||
import { Wallet } from "./services/near-wallet"; | ||
import { NFTView } from "./components/NFT"; | ||
import { EthereumView } from "./components/Ethereum"; | ||
|
||
// CONSTANTS | ||
const NFT_CONTRACT = 'v2.nft.kagi.testnet'; | ||
|
||
// NEAR WALLET | ||
const wallet = new Wallet({ network: 'testnet', createAccessKeyFor: NFT_CONTRACT }); | ||
|
||
function App() { | ||
const [signedAccountId, setSignedAccountId] = useState(''); | ||
const [status, setStatus] = useState("Please login to request a signature"); | ||
const [tokenId, setTokenId] = useState(''); | ||
const [transactionHash, setTransactionHash] = useState(''); | ||
|
||
useEffect(() => { | ||
wallet.startUp(setSignedAccountId) | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (signedAccountId && tokenId == '') { | ||
setStatus("Please select a NFT"); | ||
} else if (! signedAccountId) { | ||
setStatus("Please login to request a signature"); | ||
} | ||
}, [signedAccountId]); | ||
|
||
|
||
useEffect(() => { | ||
// Get transaction hash from URL when using web wallet | ||
const getTransactionHashFromUrl = () => { | ||
const urlParams = new URLSearchParams(window.location.search); | ||
const hash = urlParams.get('transactionHashes'); | ||
setTransactionHash(hash); | ||
}; | ||
|
||
getTransactionHashFromUrl(); | ||
}, []); | ||
|
||
return ( | ||
<NearContext.Provider value={{ wallet, signedAccountId, tokenId, setTokenId }}> | ||
<Navbar /> | ||
<div className="container"> | ||
<h4> 🔗 Ethereum NFT accounts </h4> | ||
<p className="small"> | ||
Send Ethereum transactions through NFT Chain Keys. Learn more in the <a href="https://docs.near.org/build/chain-abstraction/nft-chain-keys"> <b>documentation</b></a>. | ||
</p> | ||
|
||
{signedAccountId && | ||
<div style={{ width: '50%', minWidth: '400px' }}> | ||
|
||
<div className="input-group input-group-sm mt-3 mb-4"> | ||
<input className="form-control text-center" type="text" value={`NFT Chain Keys Contract: ${NFT_CONTRACT}`} disabled /> | ||
</div> | ||
|
||
<NFTView props={{ NFT_CONTRACT }} /> | ||
|
||
<EthereumView props={{ setStatus, NFT_CONTRACT, transactionHash }} /> | ||
</div> | ||
} | ||
|
||
<div className="mt-3 small text-center"> | ||
{status} | ||
</div> | ||
</div> | ||
</NearContext.Provider> | ||
) | ||
} | ||
|
||
export default App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
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'; | ||
|
||
const Sepolia = 11155111; | ||
const Eth = new Ethereum('https://rpc2.sepolia.org', Sepolia); | ||
|
||
export function EthereumView({ props: { setStatus, NFT_CONTRACT, transactionHash } }) { | ||
const { wallet, signedAccountId, tokenId, setTokenId } = useContext(NearContext); | ||
|
||
const [receiver, setReceiver] = useState("0xe0f3B7e68151E9306727104973752A415c2bcbEb"); | ||
const [amount, setAmount] = useState(0.01); | ||
const [loading, setLoading] = useState(false); | ||
const [step, setStep] = useState("request"); | ||
const [signedTransaction, setSignedTransaction] = useState(null); | ||
const [senderAddress, setSenderAddress] = useState(''); | ||
|
||
const [derivation, setDerivation] = useState("ethereum-1"); | ||
const derivation_path = useDebounce(derivation, 1000); | ||
|
||
useEffect(() => { | ||
// Only called if using web wallet | ||
if (transactionHash != null && sessionStorage.getItem('chain') == "ETH") { | ||
resetParams(); | ||
handleCallback(); | ||
} | ||
}, [transactionHash]); | ||
|
||
// Reset params to before asking for signature if using web wallet | ||
async function resetParams() { | ||
try { | ||
const args = await wallet.getTransactionArgs(transactionHash); | ||
setTokenId(args.token_id); | ||
setDerivation(args.path); | ||
setAmount(sessionStorage.getItem('amount')); | ||
} catch (e) { | ||
setStatus(`❌ Error: ${e.message}`); | ||
} | ||
} | ||
|
||
// Handles the rest of the signature method if using web wallet | ||
async function handleCallback() { | ||
try { | ||
setLoading(true); | ||
const signedTransaction = await Eth.requestSignatureFromNFTCallback(wallet, transactionHash); | ||
setSignedTransaction(signedTransaction); | ||
setStatus(`✅ Signed payload ready to be relayed to the Ethereum network`); | ||
setStep('relay'); | ||
setLoading(false); | ||
} catch (e) { | ||
setStatus(`❌ Error: ${e.message}`); | ||
setLoading(false); | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
setSenderAddress('Waiting for you to stop typing...') | ||
}, [derivation]); | ||
|
||
useEffect(() => { | ||
if (tokenId == '') { | ||
setSenderAddress('Select NFT'); | ||
} else { | ||
setEthAddress(); | ||
} | ||
|
||
async function setEthAddress() { | ||
setStatus('Querying your address and balance'); | ||
setSenderAddress(`Deriving address from path ${derivation_path}...`); | ||
|
||
const { address } = await Eth.deriveAddress(NFT_CONTRACT, derivation_path, tokenId); | ||
setSenderAddress(address); | ||
|
||
const balance = await Eth.getBalance(address); | ||
setStatus(`Your Ethereum address is: ${address}, balance: ${balance} ETH`); | ||
} | ||
}, [signedAccountId, derivation_path, tokenId]); | ||
|
||
async function chainSignature() { | ||
setStatus('🏗️ Creating transaction'); | ||
const { transaction, payload } = await Eth.createPayload(senderAddress, receiver, amount); | ||
|
||
setStatus(`🕒 Asking ${NFT_CONTRACT} to sign the transaction, this might take a while`); | ||
try { | ||
const signedTransaction = await Eth.requestSignatureFromNFT(wallet, tokenId, NFT_CONTRACT, derivation_path, payload, transaction, senderAddress); | ||
setSignedTransaction(signedTransaction); | ||
setStatus(`✅ Signed payload ready to be relayed to the Ethereum network`); | ||
setStep('relay'); | ||
} catch (e) { | ||
setStatus(`❌ Error: ${e.message}`); | ||
setLoading(false); | ||
} | ||
} | ||
|
||
async function relayTransaction() { | ||
setLoading(true); | ||
setStatus('🔗 Relaying transaction to the Ethereum network... this might take a while'); | ||
|
||
try { | ||
const txHash = await Eth.relayTransaction(signedTransaction); | ||
setStatus(<> | ||
<a href={`https://sepolia.etherscan.io/tx/${txHash}`} target="_blank"> ✅ Successful </a> | ||
</> | ||
); | ||
} catch (e) { | ||
setStatus(`❌ Error: ${e.message}`); | ||
} | ||
|
||
setStep('request'); | ||
setLoading(false); | ||
window.history.pushState({}, '', window.location.origin); | ||
} | ||
|
||
const UIChainSignature = async () => { | ||
setLoading(true); | ||
await chainSignature(); | ||
setLoading(false); | ||
} | ||
|
||
return ( | ||
<> | ||
<div className="row mb-3"> | ||
<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={(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} /> | ||
</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} /> | ||
<div className="form-text"> Ethereum units </div> | ||
</div> | ||
</div> | ||
|
||
<div className="text-center"> | ||
{step === 'request' && <button className="btn btn-primary text-center" onClick={UIChainSignature} disabled={loading}> Request Signature </button>} | ||
{step === 'relay' && <button className="btn btn-success text-center" onClick={relayTransaction} disabled={loading}> Relay Transaction </button>} | ||
</div> | ||
|
||
</> | ||
) | ||
} | ||
|
||
EthereumView.propTypes = { | ||
props: PropTypes.shape({ | ||
setStatus: PropTypes.func.isRequired, | ||
NFT_CONTRACT: PropTypes.string.isRequired, | ||
}).isRequired | ||
}; |
Oops, something went wrong.