Skip to content

Latest commit

 

History

History
355 lines (258 loc) · 20.3 KB

frontend-creation.md

File metadata and controls

355 lines (258 loc) · 20.3 KB

Creating a Gravity Bridge frontend

This document covers how to interact with Gravity Bridge as a frontend integrator.

There are two major supported usecases, a 'local' frontend will communicate directly with Gravity Bridge RPC and provide the most visibility into the workings of the bridge.

A 'remote' frontend only interacts with a destination blockchain, this chain will have an IBC channel to Gravity Bridge and you as an application developer will be able to send tokens to and from Ethereum without having to interact with Gravity Bridge RPC.

Index

Getting started

Sending tokens from Ethereum to Gravity Bridge

Sending tokens from Gravity Bridge to Ethereum

Sending tokens from Ethereum through Gravity Bridge to another chain

Sending tokens from another chain through Gravity Bridge to Ethereum

Monitoring a send from Ethereum to Gravity Bridge

Monitoring a send from Gravity Bridge to Ethereum

Canceling a send from Gravity Bridge to Ethereum

Getting started with CosmosSDK development

This section tries to provide a very basic best-effort guide at getting started with CosmosSDK frontend development. It is by no means completely but is hopefully helpful.

Keplr is by far the most common wallet for end users.

Keplr with Ledger support is the gold standard for UX + Security at the moment.

CosmosSDK currently has 3 distinct RPC endpoints that can be used to query data and send transactions

  • Legacy Amino which is deprecated and will hopefully be removed soon
  • ABCI ABCI is poorly documented but is heavily used by most clients
  • gRPC is the modern endpoint. If you're starting development now you should use gRPC, the question is exactly how.

We provide gRPCWeb support at https://gravitychain.io:9091, gRPC web has a pretty steep setup curve mainly revolving around the proto file definitions.

In our documentation we will provide some REST compatible ABCI calls to query basic information, but any more advanced querying of Gravity Bridge should take the time to setup gRPC web, it will pay off very quickly if you are trying to process and interpret many large and complex data structures at once.

For events a websocket protocol endpoint is available at wss://gravitychain.io:26657. Contrary to the events documentation typed events are deployed and in use on Gravity Bridge. When it comes to interpreting these events you will need to have the proto file definitions handy.

The server at gravitychain.io also provides ABCI and Legacy Amino endpoints see the resources doc for more details.

Sending tokens from Ethereum to Gravity Bridge

In order to interact with the Gravity Bridge Ethereum contract you will need to initialize the users Ethereum wallet then using the information provided by the user encode the contract call before finally sending a transaction with the encoded contract call set as the transaction bytes.

The Gravity Bridge contract ABI is available as Gravity.json attached to every release

0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906 is the Gravity Bridge contract address.

The first transaction you will have to send is an ERC20 approve() tx. This will allow the Gravity Bridge contract to remove the ERC20 token from the users wallet and lock it in the bridge. To do this you will need to load the ERC20 contract standard ABI, encode the approve, and send it on behalf of the user with the Gravity Bridge contract address as a parameter. If you do not do this the Gravity Bridge contract will not be able to lock up the users ERC20 tokens.

Once approve() has passed the next (and last step) is to call SendToCosmos call takes

 function sendToCosmos(
  address _tokenContract,
  string calldata _destination,
  uint256 _amount

This will lock up amount of hte users ERC20 tokens as identified by tokenContract value. The destination is a bech32 encoded Gravity address in the form of gravity1<HASH>.

Once the transaction has been submitted and succeeded the tokens should be available on Gravity Bridge within 50 Ethereum blocks. This is about 10 minutes. This delay is for finality and will be removed once Ethereum moves to proof of stake.

In the Gravity Bridge events section we discuss some events that provide more insight into how this deposit is being processed, the only event fired by a deposit on the Ethereum side is the SendToCosmosEvent which tells you that your Ethereum transaction succeeded and starts the fifty block delay period.

The section Monitoring a send from Ethereum to Gravity Bridge contains more info about how to monitor the process from here. But for the most part this won't be required, the tokens will be available on Gravity Bridge in less than 20 minutes with no further action from the user.

Metamask Example

See the Metamask docs and Web3 docs for more context. Note that you could easily use Web3.js to send the transaction if you don't need Metamask to access keys. This code will create your transaction and prompt the user to sign and send.

Once this tx is broadcasted on Ethereum tokens will be delivered directly to the target address on Gravity Bridge

// Gravity Bridge contract address
let gravityBridge = "0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"
// USDC ERC20 contract address
let erc20 = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
let destination = "gravity18xvpj53vaupyfejpws5sktv5lnas5xj2kwracl"
// amount of USDC to send, since USDC has 6 decimals divide any value here by 1*10^decimals for the
// whole number of USDC. You should use the ERC20 decimals() call for this since tokens have different values
let amount = 50000

// approve must only be called once on the ERC20, in this example
// we limit the amount sent to the exact user send. In practice you may
// want to set a very large amount to save on gas re-sending a new approval
// each time.
let approve = web3.eth.abi.encodeFunctionCall({
    name: 'approve',
    type: 'function',
    inputs: [
    {
        type: 'address',
        name: 'spender'
    },
    {
        type: 'uint256',
        name: 'amount'
    }
    ]
}, [erc20, amount]);
// the actual send to cosmos function call that will lock
// the tokens for transfer
let sendToCosmos = web3.eth.abi.encodeFunctionCall({
    name: 'sendToCosmos',
    type: 'function',
    inputs: [
    {
        type: 'address',
        name: '_tokenContract'
    },
    {
        type: 'string',
        name: '_destination'
    },
    {
        type: 'uint256',
        name: '_amount'
    }
    ]
}, [erc20, destination, amount]);
const approveTransactionParameters = {
  to: erc20,
  from: ethereum.selectedAddress,
  value: '0x00',
  data: approve,
};
const txHash = await ethereum.request({
  method: 'eth_sendTransaction',
  params: [approveTransactionParameters],
});
const sendTransactionParameters = {
  to: gravityBridge,
  from: ethereum.selectedAddress,
  value: '0x00',
  data: sendToCosmos,
};
const txHash = await ethereum.request({
  method: 'eth_sendTransaction',
  params: [sendTransactionParameters],
});

Sending tokens from Gravity Bridge to Ethereum

Sending tokens to Ethereum does not have a predictable timeline like sending tokens to Cosmos. In exchange for dramatically lower fees relayers will choose when to request a batch containing your users tx and many other people's transactions to relay.

You can imagine Gravity Bridge batch fees as a metaphorical bus stop. Various people wishing to travel walk to the bus stop and put money into a jar. When enough money to pay for the bus driver (relayer) has been collected, the travelers board the bus and it departs for Ethereum. You can supply any fee you like, all the way down to zero, in which case the users tx will be picked up for free when other profitable transactions fill the metaphorical jar and make it worth the relayers time.

The bridge fee must be in the same token you are sending across the bridge.

Suggested fees:

  • Someday: Zero fee
  • Within a day: 50k Ethereum gas worth
  • Within an hour: 200k Ethereum gas worth
  • Instantly: 500k Ethereum gas worth

In order to convert these values to ETH you should query the eth_gasPrice endpoint, and multiply the resulting value by the suggested values above.

$$fee_cost_in_wei = eth_gasPrice * gasAmount$$

This will give you a gas cost in wei, convert this value to the equivalent cost in the bridged token using a price oracle.

Once the user has selected their fee amount you can actually form and submit the MsgSendToEth transaction.

Keep in mind

  • bridge_fee must be the same token as amount
  • This transaction still has a 'fee' field for Gravity Bridge fees, this can be set to zero for now
  • eth_dest should be a correctly capitalized eip-55 address

Once the user has sent this message the tokens will be removed from their account, but they will not appear on Ethereum until the transaction batch is relayed.

Please see the sections, for more info.

Monitoring a send from Gravity Bridge to Ethereum Canceling a send from Gravity Bridge to Ethereum

Sending tokens from Ethereum through Gravity Bridge to another chain

This feature has been available since the Mercury upgrade and is extremely straightforward to use. Following the steps outlined in Sending tokens from Ethereum to Gravity Bridge simply replace the gravity1<hex> bech32 encoded address with the chain prefix of your choice for example a deposit to osmosis1 will go to Osmosis and cosmos1 will go to the Cosmos Hub.

This does require some setup though. Before you can use this feature you must make sure the address prefix you are interested in using has been mapped to an IBC channel using the command gravity tx bech32ibc update-hrp-ibc-record to create a governance proposal

Once this proposal has passed simply call Gravity.sol sendToCosmos endpoint with a native address (for example one starting with cosmos1) and the transaction will be instantly forwarded.

Metamask Example Remote Send

See the Metamask docs and Web3 docs for more context. Note that you could easily use Web3.js to send the transaction if you don't need Metamask to access keys. This code will create your transaction and prompt the user to sign and send.

Once this tx is broadcasted on Ethereum tokens will be delivered directly to the target chain as routed via the prefix.

// Gravity Bridge contract address
let gravityBridge = "0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"
// USDC ERC20 contract address
let erc20 = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
// destination using destination chain prefix. If the prefix has not been configured
// for forwarding the address will be re-prefixed to gravity and funds will be left
// there for the user to pick up
let destination = "osmo18xvpj53vaupyfejpws5sktv5lnas5xj2kwracl"
// amount of USDC to send, since USDC has 6 decimals divide any value here by 1*10^decimals for the
// whole number of USDC. You should use the ERC20 decimals() call for this since tokens have different values
let amount = 50000

// approve must only be called once on the ERC20, in this example
// we limit the amount sent to the exact user send. In practice you may
// want to set a very large amount to save on gas re-sending a new approval
// each time.
let approve = web3.eth.abi.encodeFunctionCall({
    name: 'approve',
    type: 'function',
    inputs: [
    {
        type: 'address',
        name: 'spender'
    },
    {
        type: 'uint256',
        name: 'amount'
    }
    ]
}, [erc20, amount]);
// the actual end to comsos function call that will lock
// the tokens for transfer
let sendToCosmos = web3.eth.abi.encodeFunctionCall({
    name: 'sendToCosmos',
    type: 'function',
    inputs: [
    {
        type: 'address',
        name: '_tokenContract'
    },
    {
        type: 'string',
        name: '_destination'
    },
    {
        type: 'uint256',
        name: '_amount'
    }
    ]
}, [erc20, destination, amount]);
const approveTransactionParameters = {
  to: erc20,
  from: ethereum.selectedAddress,
  value: '0x00',
  data: approve,
};
const txHash = await ethereum.request({
  method: 'eth_sendTransaction',
  params: [approveTransactionParameters],
});
const sendTransactionParameters = {
  to: gravityBridge,
  from: ethereum.selectedAddress,
  value: '0x00',
  data: sendToCosmos,
};
const txHash = await ethereum.request({
  method: 'eth_sendTransaction',
  params: [sendTransactionParameters],
});

Sending tokens from another chain through Gravity Bridge and to Ethereum

This feature will require interchain accounts to be integrated into Gravity Bridge and the destination chain you will be integrating against. Gravity Bridge does not currently have interchain accounts support.

Monitoring a send from Gravity Bridge To Ethereum

The lifecycle of a MsgSendToEth transaction is as follows.

  • User sends MsgSendToEth, removing the tokens from their balance
  • the transaction enters the SendToEth transaction pool for that token type
  • a relayer looks at the total fees for the transaction pool for that token type
  • a relayer requests a batch be created
  • validators sign the batch
  • the batch can then be relayed, or will time out in about 8 hours, returning the MsgSendToEth to the first step

Any time the MsgSendToEth transaction is in the transaction pool, and not in a signed batch, it is possible to cancel the send and have the user get an instant refund.

Once a MsgSendToEth is in a batch it is possible that it has been submitted to Ethereum, to prevent double spending it is not possible to cancel the send until the batch has timed out. This timeout value is a number of blocks and is set in the Gravity params. Due to the complexity of knowing exactly what block it is on Ethereum at any given time the batch will not complete timing out until any Gravity Bridge event is processed by the oracle with a later block height. For example a deposit of an unrelated token will complete a batch timeout by proving to Gravity Bridge that the Ethereum block height has exceeded the batch timeout height.

Query endpoints related to batches and essentially all internal functions of Gravity Bridge are currently GRPC only. We will be adding ABCI query endpoints for commonly used functionality but encourage use of gRPC-web where feasible.

In order to determine the status of a user transaction you should check.

  • if the users transaction is in a batch
  • the total fee value for the transaction pool of that token type

This will let you display if the user can cancel their transaction, and predict at least to some degree when a batch might be created and relayed.

QueryPendingSendToEth will return all MsgSendToEth transactions sent by this user and indicate if they are currently in a batch.

QueryBatchFeeRequest will return a list of fee totals for all token types currently being bridged.

Translating QueryBatchFeeRequest into usable data takes a little work. First you must locate the token that is being bridged in the response. Then take a look at the total fees for that batch, convert these total fees to equal value in WETH and finally estimate what the cost of executing the batch would be.

It's infeasible to create a perfect fee estimation, so go with this heuristic. A batch starts at 400,000 Ethereum gas, and goes up in cost by 16,000 Ethereum gas per transaction included.

So a batch with 50 transactions would estimate at 1,200,000 Ethereum gas.

This gas estimate can be multiplied by the current gas price and then compared to the total fee value in WETH to determine if a batch will be relayed soon.

The effectiveness of any time estimations will be greatly improved by a historical gas price oracle which will allow you to easily figure out when fee value and gas prices intersect.

Finally we have to detect when a batch is actually relayed.

Depending on which is easier for you there are two methods to do this.

Either will allow you to monitor batch execution and confirm to the user that their funds are available on Ethereum.

Monitoring a send from Ethereum to Gravity Bridge

Once a MsgSendToCosmos transaction has been included in an Ethereum block the process for the rest of the deposit is hands off for the user.

In order to display progress to the user you should.

  • Query the txid and show the user that the transaction is waiting on Ethereum
  • Once the transaction is included in a block display a 50 block countdown until funds are available on Gravity Bridge

If you want to have even more insight into the process on Gravity Bridge you can use QueryAttestationsRequest to observe the inner workings fo the validators confirming the deposit. This will not start until after 50 blocks have elapsed and should take only 10-20 seconds start to finish.

Observing the attestation process is probably not worth it from a UI perspective, but you can if you like. The attestation query endpoint may be more useful to observe when a batch has been relayed.

Canceling a send from Gravity Bridge To Ethereum

In order to cancel a MsgSendToEth you must first check if the transaction is in a batch using.

QueryPendingSendToEth.

If the MsgSendToEth is in a batch, you must wait for that batch to time out before canceling is possible. The batch may also be executed, at which point the tokens are on Ethereum and canceling the operation is not possible.

Once you have determined that the MsgSendToEth is not in a batch you can retrieve it's transaction_id and send a MsgCancelSendToEth which will instantly cancel the transaction and refund the user.