This section goes through the same instructions as the previous section, replacing GatewayJS with RenJS. The interface is similar, so they can easily be interchanged.
Install RenJS using npm
:
npm install --save @renproject/ren
yarn add @renproject/ren
Import it at the top of your file with:
src/App.jsimport RenJS from "@renproject/ren";
Initialize it and add it to your App.js
state (line 17
, after message: "",
) like so:
renJS: new RenJS("testnet"),
We initialize it using the network "testnet"
. This will tell it to talk to RenVM Testnet and to use Ethereum Kovan.
We are now ready to call the deposit
function.
First, we need to create a Mint
object. RenJS will use this Mint
object to understand exactly how we want to transfer our BTC into Ethereum. Add this to the body of deposit
:
const { web3, renJS } = this.state;const amount = 0.003; // BTC​const mint = renJS.lockAndMint({// Send BTC from the Bitcoin blockchain to the Ethereum blockchain.sendToken: RenJS.Tokens.BTC.Btc2Eth,​// The contract we want to interact withsendTo: contractAddress,​// The name of the function we want to callcontractFn: "deposit",nonce: renJS.utils.randomNonce(),​// Arguments expected for calling `deposit`contractParams: [{name: "_msg",type: "bytes",value: web3.utils.fromAscii(`Depositing ${amount} BTC`),}],// Web3 provider for submitting mint to Ethereumweb3Provider: web3.currentProvider,});
We tell it what asset we are shifting and give it the details it needs to talk to our Ethereum contract.
Notice that we don't provide an amount - it will accept the first deposit sent to it. If you require a specific amount, you can include: requiredAmount: Math.floor(amount * (10 ** 8))
. The downside is that if the user sends an incorrect amount, RenJS will currently ignore the deposit.
Next, we want to generate a gateway address. This is generated uniquely from the details we provided to the Mint
object. We show the gateway address to the user and then wait for them to send BTC to this address.
// Show the gateway address to the user so that they can transfer their BTC to it.const gatewayAddress = await mint.gatewayAddress();this.log(`Deposit ${amount} BTC to ${gatewayAddress}`);​// Wait for the Darknodes to detect the BTC transfer.const confirmations = 0;const deposit = await mint.wait(confirmations);
After we have received the BTC deposit, we need to let RenVM know about it. RenVM will check the gateway address and return a signature that authorizes us to shift the BTC into Ethereum.
// Retrieve signature from RenVM.this.log("Submitting to RenVM...");const signature = await deposit.submit();
Once we have the signature, we need to pass it to our Basic
contract:
// Submit the signature to Ethereum and receive zBTC.this.log("Submitting to smart contract...");await signature.submitToEthereum(web3.currentProvider);this.log(`Deposited ${amount} BTC.`);
When transferring BTC out of Ethereum back to the Bitcoin blockchain, we need to burn the renBTC tokens. RenVM will automatically see this and release the BTC. We use a regular Web3 contract call to do this first step. Add this to withdraw
:
const { web3, renJS, balance } = this.state;​const amount = balance;const recipient = prompt("Enter BTC recipient:");const from = (await web3.eth.getAccounts())[0];const contract = new web3.eth.Contract(ABI, contractAddress);​this.log("Calling `withdraw` on smart contract...");const ethereumTxHash = await new Promise((resolve, reject) => {contract.methods.withdraw(web3.utils.fromAscii(`Withdrawing ${amount} BTC`), // _msgRenJS.utils.btc.addressToHex(recipient), //_toMath.floor(amount * (10 ** 8)), // _amount in Satoshis).send({ from }).on("transactionHash", resolve).catch(reject);});
Once we have the transaction hash of the contract call, we can ask RenVM about the status of the transaction. They will return back the final amount and address that received the BTC.
this.log(`Retrieving burn event from contract...`);const burn = await renJS.burnAndRelease({// Send BTC from the Ethereum blockchain to the Bitcoin blockchain.// This is the reverse of shitIn.sendToken: RenJS.Tokens.BTC.Eth2Btc,​// The web3 provider to talk to Ethereumweb3Provider: web3.currentProvider,​// The transaction hash of our contract callethereumTxHash,}).readFromEthereum();​this.log(`Submitting to Darknodes...`);await burn.submit();this.log(`Withdrew ${amount} BTC to ${recipient}.`);
Your App.js
file should now look like this (don't forget to replace the contract address):
src/App.jsimport React from 'react';import RenJS from "@renproject/ren";import Web3 from "web3";import './App.css';​import ABI from "./ABI.json";​// Replace with your contract's address.const contractAddress = "0xb9b0442DE9BC214a23B434Ee2Ec7AF8A4e1b3eeE";​class App extends React.Component {constructor(props) {super(props);this.state = {balance: 0,message: "",error: "",renJS: new RenJS("testnet"),}}​componentDidMount = async () => {let web3Provider;​// Initialize web3 (https://medium.com/coinmonks/web3-js-ethereum-javascript-api-72f7b22e2f0a)// Modern dApp browsers...if (window.ethereum) {web3Provider = window.ethereum;try {// Request account accessawait window.ethereum.enable();} catch (error) {// User denied account access...this.logError("Please allow access to your Web3 wallet.");return;}}// Legacy dApp browsers...else if (window.web3) {web3Provider = window.web3.currentProvider;}// If no injected web3 instance is detected, fall back to Ganacheelse {this.logError("Please install MetaMask!");return;}​const web3 = new Web3(web3Provider);​const networkID = await web3.eth.net.getId();if (networkID !== 42) {this.logError("Please set your network to Kovan.");return;}​this.setState({ web3 }, () => {​// Update balances immediately and every 10 secondsthis.updateBalance();setInterval(() => {this.updateBalance();}, 10 * 1000);});}​render = () => {const { balance, message, error } = this.state;return (<div className="App"><p>Balance: {balance} BTC</p><p><button onClick={() => this.deposit().catch(this.logError)}>Deposit 0.003 BTC</button></p><p><button onClick={() => this.withdraw().catch(this.logError)}>Withdraw {balance} BTC</button></p><p>{message}</p>{error ? <p style={{ color: "red" }}>{error}</p> : null}</div>);}​updateBalance = async () => {const { web3 } = this.state;const contract = new web3.eth.Contract(ABI, contractAddress);const balance = await contract.methods.balance().call();this.setState({ balance: parseInt(balance.toString()) / 10 ** 8 });}​logError = (error) => {console.error(error);this.setState({ error: String((error || {}).message || error) });}​log = (message) => {this.setState({ message });}​deposit = async () => {this.logError(""); // Reset error​const { web3, renJS } = this.state;const amount = 0.003; // BTC​const mint = renJS.lockAndMint({// Send BTC from the Bitcoin blockchain to the Ethereum blockchain.sendToken: RenJS.Tokens.BTC.Btc2Eth,​// The contract we want to interact withsendTo: contractAddress,​// The name of the function we want to callcontractFn: "deposit",nonce: renJS.utils.randomNonce(),​// Arguments expected for calling `deposit`contractParams: [{name: "_msg",type: "bytes",value: web3.utils.fromAscii(`Depositing ${amount} BTC`),}],// Web3 provider for submitting mint to Ethereumweb3Provider: web3.currentProvider,});​// Show the gateway address to the user so that they can transfer their BTC to it.const gatewayAddress = await mint.gatewayAddress();this.log(`Deposit ${amount} BTC to ${gatewayAddress}`);​// Wait for the Darknodes to detect the BTC transfer.const confirmations = 0;const deposit = await mint.wait(confirmations);​// Retrieve signature from RenVM.this.log("Submitting to RenVM...");const signature = await deposit.submit();​// Submit the signature to Ethereum and receive zBTC.this.log("Submitting to smart contract...");await signature.submitToEthereum(web3.currentProvider);this.log(`Deposited ${amount} BTC.`);}​withdraw = async () => {this.logError(""); // Reset error​const { web3, renJS, balance } = this.state;​const amount = balance;const recipient = prompt("Enter BTC recipient:");const from = (await web3.eth.getAccounts())[0];const contract = new web3.eth.Contract(ABI, contractAddress);​this.log("Calling `withdraw` on smart contract...");const ethereumTxHash = await new Promise((resolve, reject) => {contract.methods.withdraw(web3.utils.fromAscii(`Depositing ${amount} BTC`), // _msgRenJS.utils.btc.addressToHex(recipient), //_toMath.floor(amount * (10 ** 8)), // _amount in Satoshis).send({ from }).on("transactionHash", resolve).catch(reject);});​this.log(`Retrieving burn event from contract...`);const burn = await renJS.burnAndRelease({// Send BTC from the Ethereum blockchain to the Bitcoin blockchain.// This is the reverse of shitIn.sendToken: RenJS.Tokens.BTC.Eth2Btc,​// The web3 provider to talk to Ethereumweb3Provider: web3.currentProvider,​// The transaction hash of our contract callethereumTxHash,}).readFromEthereum();​this.log(`Submitting to Darknodes...`);await burn.submit();this.log(`Withdrew ${amount} BTC to ${recipient}.`);}}​export default App;
If you click "Deposit 0.003 BTC", it should show you a Bitcoin address:
If you don't already have Testnet BTC, you'll need to go to a Bitcoin Testnet faucet (we recommend https://testnet-faucet.mempool.co).
Send 0.003 BTC
to the Bitcoin gateway address to continue. RenJS will wait for two confirmations before continuing, which may take around 15-20 minutes.
After completing all the steps, you should see the balance increase by 0.003 BTC
, minus fees (currently a 0.001 BTC
transfer fee and a 0.25% RenVM fee).
Test withdrawing as well. If you don't have a Testnet BTC address to give it, the faucet lists a return address you can use. You should see the balance go back to 0 and the Bitcoin address you entered receive the BTC (again, some transfer fees will be deducted).
You can find the site deployed here as well: https://renproject.github.io/renjs-tutorial​