Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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,6 +13,7 @@
"@ethereumjs/common": "^4.3.0",
"@ethereumjs/tx": "^5.3.0",
"@ethereumjs/util": "^9.0.3",
"@mysten/sui": "^1.30.4",
"@near-wallet-selector/bitte-wallet": "^8.9.13",
"@near-wallet-selector/core": "^8.9.13",
"@near-wallet-selector/here-wallet": "^8.9.13",
Expand All @@ -26,7 +27,7 @@
"bitcoinjs-lib": "^6.1.5",
"bn.js": "^5.2.1",
"bs58check": "^3.0.1",
"chainsig.js": "^1.1.0",
"chainsig.js": "^1.1.7",
"elliptic": "^6.5.5",
"ethers": "^6.11.1",
"hash.js": "^1.1.7",
Expand Down
5 changes: 5 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BitcoinView } from './components/Bitcoin';
import { explorerForChain, MPC_CONTRACT, RPCforChain } from './config';
import { useWalletSelector } from '@near-wallet-selector/react-hook';
import { SolanaView } from './components/Solana';
import { SuiView } from './components/Sui';


function App() {
Expand Down Expand Up @@ -67,6 +68,7 @@ function App() {
<option value='base'> Ξ Base </option>
<option value='btc'> ₿ BTC </option>
<option value='sol'> 🪙 Solana </option>
<option value='sui'> 🪙 Sui </option>
</select>
</div>

Expand All @@ -84,6 +86,9 @@ function App() {
{chain === 'sol' && (
<SolanaView props={{setStatus}}></SolanaView>
)}
{chain === 'sui' && (
<SuiView props={{setStatus}} />
)}
</div>
</div>
)}
Expand Down
237 changes: 237 additions & 0 deletions src/components/Sui.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
import PropTypes from "prop-types";

import { useWalletSelector } from "@near-wallet-selector/react-hook";
import { useEffect, useState } from "react";
import { useDebounce } from "../hooks/debounce";
import { SIGNET_CONTRACT } from "../config";
import { chainAdapters } from "chainsig.js";
import { getFullnodeUrl, SuiClient } from '@mysten/sui/client'
import { Transaction } from '@mysten/sui/transactions'
import { bigIntToDecimal } from "../utils/bigIntToDecimal";
import { decimalToBigInt } from "../utils/decimalToBigInt";

const rpcUrl = getFullnodeUrl('testnet')
const suiClient = new SuiClient({ url: rpcUrl })

const Sui = new chainAdapters.sui.SUI({
client: suiClient,
contract: SIGNET_CONTRACT,
rpcUrl: rpcUrl,
})

export function SuiView({ props: { setStatus } }) {
const { signedAccountId, signAndSendTransactions } = useWalletSelector();

const [receiver, setReceiver] = useState("0x202fc1c421cbd6d84d632d62de50b90c1cf5564c36422a1cd00b5448b9e3d29f");
const [amount, setAmount] = useState(1);
const [loading, setLoading] = useState(false);
const [step, setStep] = useState("request");
const [signedTransaction, setSignedTransaction] = useState(null);
const [senderAddress, setSenderAddress] = useState("");
const [senderPK, setSenderPK] = useState("");

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

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

useEffect(() => {
setSuiAddress();

async function setSuiAddress() {
setStatus("Querying your address and balance");
setSenderAddress(`Deriving address from path ${derivationPath}...`);

const { address, publicKey } = await Sui.deriveAddressAndPublicKey(signedAccountId, derivationPath);

setSenderPK(publicKey);
setSenderAddress(address);

const balance = await Sui.getBalance(address);

setStatus(
`Your Sui address is: ${address}, balance: ${bigIntToDecimal(balance.balance, balance.decimals)} SUI`
);
}
}, [signedAccountId, derivationPath, setStatus]);

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

const tx = new Transaction()

const [coin] = tx.splitCoins(tx.gas, [decimalToBigInt(amount, 9)])

tx.transferObjects(
[coin],
receiver
)
tx.setSender(senderAddress)

const { hashesToSign, transaction } = await Sui.prepareTransactionForSigning(tx)


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

try {
const rsvSignatures = await SIGNET_CONTRACT.sign({
payloads: hashesToSign,
path: derivationPath,
keyType: "Eddsa",
signerAccount: {
accountId: signedAccountId,
signAndSendTransactions
}
});

if (!rsvSignatures[0] || !rsvSignatures[0].signature) {
throw new Error("Failed to sign transaction");
}

const txSerialized = Sui.finalizeTransactionSigning({
transaction,
rsvSignatures: rsvSignatures[0],
publicKey: senderPK
})

setStatus("✅ Signed payload ready to be relayed to the Sui network");
setSignedTransaction(txSerialized);
setStep("relay");
} catch (e) {
console.log(e);
setStatus(`❌ Error: ${e.message}`);
setLoading(false);
}
}

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

try {

const txHash = await Sui.broadcastTx(signedTransaction);

setStatus(
<>
<a
href={`https://suiscan.xyz/testnet/tx/${txHash.hash}`}
target="_blank"
>
{" "}
✅ Successfully Broadcasted{" "}
</a>
</>
);
} catch (e) {
setStatus(`❌ Error: ${e.message}`);
}

setStep("request");
setLoading(false);
}

const UIChainSignature = async () => {
setLoading(true);
await chainSignature();
setLoading(false);
};

return (<>
<div className="alert alert-info text-center" role="alert">
You are working with <strong>DevTest</strong>.
<br />
You can get funds from the faucet:
<a
href="https://faucet.sui.io/"
target="_blank"
rel="noopener noreferrer"
className="alert-link"
>
faucet.sui.io/
</a>
</div>
<div className="row my-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.1"
min="0"
disabled={loading}
/>
<div className="form-text"> sui units </div>
</div>
</div>

<div className="text-center mt-3">
{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>
</>)
}

SuiView.propTypes = {
props: PropTypes.shape({
setStatus: PropTypes.func.isRequired,
}).isRequired,
};