RenJS

Part 3 of 3 - low level option

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.

Initializing RenJS

Install RenJS using npm:

npm
yarn
npm
npm install --save @renproject/ren
yarn
yarn add @renproject/ren

Import it at the top of your file with:

src/App.js
import 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.

Depositing funds

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.001; // 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 with
sendTo: contractAddress,
// The name of the function we want to call
contractFn: "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 Ethereum
web3Provider: 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.`);

Withdrawing funds

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`), // _msg
RenJS.utils.btc.addressToHex(recipient), //_to
Math.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 Ethereum
web3Provider: web3.currentProvider,
// The transaction hash of our contract call
ethereumTxHash,
}).readFromEthereum();
this.log(`Submitting to Darknodes...`);
await burn.submit();
this.log(`Withdrew ${amount} BTC to ${recipient}.`);

Complete App.js

Your App.js file should now look like this (don't forget to replace the contract address):

src/App.js
import 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 access
await 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 Ganache
else {
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 seconds
this.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.001 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.001; // 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 with
sendTo: contractAddress,
// The name of the function we want to call
contractFn: "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 Ethereum
web3Provider: 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`), // _msg
RenJS.utils.btc.addressToHex(recipient), //_to
Math.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 Ethereum
web3Provider: web3.currentProvider,
// The transaction hash of our contract call
ethereumTxHash,
}).readFromEthereum();
this.log(`Submitting to Darknodes...`);
await burn.submit();
this.log(`Withdrew ${amount} BTC to ${recipient}.`);
}
}
export default App;

Testing it

If you click "Deposit 0.001 BTC", it should show you a Bitcoin address:

An example of our new multi-blockchain dApp

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.001 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.001 BTC, minus fees (0.00005 BTC transfer fee and 0.1% RenVM fee).

After depositing some BTC into our Ethereum contract

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