Skip to content

Commit 23dee0c

Browse files
authored
Merge pull request #71 from matiasbenary/feat/add-XRP
Feat/add xrp
2 parents a91c7fa + 34b040c commit 23dee0c

File tree

3 files changed

+230
-2
lines changed

3 files changed

+230
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"bitcoinjs-lib": "^6.1.5",
2727
"bn.js": "^5.2.1",
2828
"bs58check": "^3.0.1",
29-
"chainsig.js": "^1.1.0",
29+
"chainsig.js": "^1.1.8",
3030
"elliptic": "^6.5.5",
3131
"ethers": "^6.11.1",
3232
"hash.js": "^1.1.7",

src/App.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import { BitcoinView } from './components/Bitcoin';
66
import { explorerForChain, MPC_CONTRACT, RPCforChain } from './config';
77
import { useWalletSelector } from '@near-wallet-selector/react-hook';
88
import { SolanaView } from './components/Solana';
9+
import { XRPView } from './components/XRP';
910

1011

1112
function App() {
1213
const { signedAccountId } = useWalletSelector();
1314
const [status, setStatus] = useState('Please login to request a signature');
1415
const [chain, setChain] = useState('eth');
1516

16-
1717
return (
1818
<>
1919
<Navbar />
@@ -67,6 +67,7 @@ function App() {
6767
<option value='base'> Ξ Base </option>
6868
<option value='btc'> ₿ BTC </option>
6969
<option value='sol'> 🪙 Solana </option>
70+
<option value='xrp'> 🪙 XRP </option>
7071
</select>
7172
</div>
7273

@@ -84,6 +85,9 @@ function App() {
8485
{chain === 'sol' && (
8586
<SolanaView props={{setStatus}}></SolanaView>
8687
)}
88+
{chain === 'xrp' && (
89+
<XRPView props={{setStatus}}></XRPView>
90+
)}
8791
</div>
8892
</div>
8993
)}

src/components/XRP.jsx

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
import PropTypes from "prop-types";
2+
3+
import { useWalletSelector } from "@near-wallet-selector/react-hook";
4+
import { useEffect, useState } from "react";
5+
import { useDebounce } from "../hooks/debounce";
6+
import { SIGNET_CONTRACT } from "../config";
7+
import { chainAdapters } from "chainsig.js";
8+
import { decimalToBigInt } from "../utils/decimalToBigInt";
9+
import { bigIntToDecimal } from "../utils/bigIntToDecimal";
10+
11+
const Xrp = new chainAdapters.xrp.XRP({
12+
rpcUrl: "wss://s.altnet.rippletest.net:51233/",
13+
contract: SIGNET_CONTRACT
14+
})
15+
16+
export function XRPView({ props: { setStatus } }) {
17+
const { signedAccountId, signAndSendTransactions } = useWalletSelector();
18+
19+
const [receiver, setReceiver] = useState("rnUbjwGJzDWh66xoavXnXBt4YWdGmeyE6Z");
20+
const [amount, setAmount] = useState(1);
21+
const [loading, setLoading] = useState(false);
22+
const [step, setStep] = useState("request");
23+
const [signedTransaction, setSignedTransaction] = useState(null);
24+
const [senderAddress, setSenderAddress] = useState("");
25+
26+
const [derivation, setDerivation] = useState("xrp-1");
27+
const [senderPK, setSenderPK] = useState("");
28+
const derivationPath = useDebounce(derivation, 500);
29+
30+
useEffect(() => {
31+
setSenderAddress("Waiting for you to stop typing...");
32+
}, [derivation]);
33+
34+
useEffect(() => {
35+
setXrpAddress();
36+
37+
async function setXrpAddress() {
38+
setStatus("Querying your address and balance");
39+
setSenderAddress(`Deriving address from path ${derivationPath}...`);
40+
41+
const { publicKey, address } = await Xrp.deriveAddressAndPublicKey(signedAccountId, derivationPath);
42+
43+
setSenderAddress(address);
44+
setSenderPK(publicKey);
45+
const balance = await Xrp.getBalance(address);
46+
47+
setStatus(
48+
`Your XRP address is: ${address} balance: ${bigIntToDecimal(balance.balance, balance.decimals)} XRP`
49+
);
50+
}
51+
}, [signedAccountId, derivationPath, setStatus]);
52+
53+
async function chainSignature() {
54+
setStatus("🏗️ Creating transaction");
55+
56+
const { transaction, hashesToSign } = await Xrp.prepareTransactionForSigning({
57+
from: senderAddress,
58+
to: receiver,
59+
amount: decimalToBigInt(amount, 6).toString(),
60+
publicKey: senderPK,
61+
});
62+
63+
setStatus(
64+
"🕒 Asking MPC to sign the transaction, this might take a while..."
65+
);
66+
67+
try {
68+
const rsvSignatures = await SIGNET_CONTRACT.sign({
69+
payloads: hashesToSign,
70+
path: derivationPath,
71+
keyType: "Ecdsa",
72+
signerAccount: {
73+
accountId: signedAccountId,
74+
signAndSendTransactions
75+
}
76+
});
77+
78+
if (!rsvSignatures[0]) {
79+
throw new Error("Failed to sign transaction");
80+
}
81+
82+
const txSerialized = Xrp.finalizeTransactionSigning({
83+
transaction,
84+
rsvSignatures,
85+
})
86+
87+
setStatus("✅ Signed payload ready to be relayed to the XRP network");
88+
setSignedTransaction(txSerialized);
89+
setStep("relay");
90+
} catch (e) {
91+
console.log(e);
92+
setStatus(`❌ Error: ${e.message}`);
93+
setLoading(false);
94+
}
95+
}
96+
97+
async function relayTransaction() {
98+
setLoading(true);
99+
setStatus(
100+
"🔗 Relaying transaction to the XRP network... this might take a while"
101+
);
102+
103+
try {
104+
console.log(signedTransaction);
105+
106+
const txHash = await Xrp.broadcastTx(signedTransaction);
107+
108+
setStatus(
109+
<>
110+
<a
111+
href={`https://testnet.xrpl.org/transactions/${txHash.hash}`}
112+
target="_blank"
113+
>
114+
{" "}
115+
✅ Successfully Broadcasted{" "}
116+
</a>
117+
</>
118+
);
119+
} catch (e) {
120+
setStatus(`❌ Error: ${e.message}`);
121+
}
122+
123+
setStep("request");
124+
setLoading(false);
125+
}
126+
127+
const UIChainSignature = async () => {
128+
setLoading(true);
129+
await chainSignature();
130+
setLoading(false);
131+
};
132+
133+
return (<>
134+
<div className="alert alert-info text-center" role="alert">
135+
You are working with <strong>Testnet</strong>.
136+
<br />
137+
You can get funds from the faucet:
138+
<a
139+
href="https://xrpl.org/resources/dev-tools/xrp-faucets"
140+
target="_blank"
141+
rel="noopener noreferrer"
142+
className="alert-link"
143+
>
144+
xrpl.org/resources/dev-tools/xrp-faucets
145+
</a>
146+
</div>
147+
<div className="row my-3">
148+
<label className="col-sm-2 col-form-label col-form-label-sm">
149+
Path:
150+
</label>
151+
<div className="col-sm-10">
152+
<input
153+
type="text"
154+
className="form-control form-control-sm"
155+
value={derivation}
156+
onChange={(e) => setDerivation(e.target.value)}
157+
disabled={loading}
158+
/>
159+
<div className="form-text" id="eth-sender">
160+
{" "}
161+
{senderAddress}{" "}
162+
</div>
163+
</div>
164+
</div>
165+
<div className="row mb-3">
166+
<label className="col-sm-2 col-form-label col-form-label-sm">To:</label>
167+
<div className="col-sm-10">
168+
<input
169+
type="text"
170+
className="form-control form-control-sm"
171+
value={receiver}
172+
onChange={(e) => setReceiver(e.target.value)}
173+
disabled={loading}
174+
/>
175+
</div>
176+
</div>
177+
<div className="row mb-3">
178+
<label className="col-sm-2 col-form-label col-form-label-sm">
179+
Amount:
180+
</label>
181+
<div className="col-sm-10">
182+
<input
183+
type="number"
184+
className="form-control form-control-sm"
185+
value={amount}
186+
onChange={(e) => setAmount(e.target.value)}
187+
step="0.1"
188+
min="0"
189+
disabled={loading}
190+
/>
191+
<div className="form-text"> XRP units </div>
192+
</div>
193+
</div>
194+
195+
<div className="text-center mt-3">
196+
{step === "request" && (
197+
<button
198+
className="btn btn-primary text-center"
199+
onClick={UIChainSignature}
200+
disabled={loading}
201+
>
202+
{" "}
203+
Request Signature{" "}
204+
</button>
205+
)}
206+
{step === "relay" && (
207+
<button
208+
className="btn btn-success text-center"
209+
onClick={relayTransaction}
210+
disabled={loading}
211+
>
212+
{" "}
213+
Relay Transaction{" "}
214+
</button>
215+
)}
216+
</div>
217+
</>)
218+
}
219+
220+
XRPView.propTypes = {
221+
props: PropTypes.shape({
222+
setStatus: PropTypes.func.isRequired,
223+
}).isRequired,
224+
};

0 commit comments

Comments
 (0)