# Cross-chain transfers of custom tokens

We have previously seen how native tokens (wBTC) can be transferred across Bitcoin-IPC subnets. In addition to that, the protocol allows ERC20 tokens, deployed on a subnet, to be transferred to any other Bitcoin-IPC subnet. This page guides you through the deployment and cross-chain transfer of such tokens.

{% hint style="success" %}
This [technical video](https://www.youtube.com/watch?v=8rMrf4TYfCY) also guides you through the process of deploying and transferring ERC20 contracts, such as a stablecoin, across Bitcoin-IPC subnets (it loosely follows the steps in this guide and additionally uses Metamask to view the balances).
{% endhint %}

### **Prerequisites**

* Resuming from [Cross-chain token transfers](/ipc-btc-scaling-docs/user-guide-for-using-subnets/publish-your-docs-1.md), we assume there are two subnets running (we previously called them `SOURCE_SUBNET_ID`  and `TARGET_SUBNET_ID` ). Please now export their IDs as the more general env variables `$SUBNET_ID_1`  and  `$SUBNET_ID_2` .
* As we will be running commands inside the docker container `bitcoin-ipc`, following `RPC_URL_1` and `RPC_URL_2` .
* We have exported the IPC address of user 1 and user 2 as `$IPC_ADDRESS_OF_USER_1` and  `$IPC_ADDRESS_OF_USER_2` , respectively (the `bitcoin-ipc` container exports them by default).
* All together:

```bash
export SUBNET_ID_1=$SOURCE_SUBNET_ID
export SUBNET_ID_2=$TARGET_SUBNET_ID
export RPC_URL_1=http://host.docker.internal:8545
export RPC_URL_2=http://host.docker.internal:9545
export ISSUER_ADDR=$IPC_ADDRESS_OF_VALIDATOR_1
```

{% hint style="info" %}
Run all following commands from inside the `bitcoin-ipc` container, except if you used the Alternative deployment method.
{% endhint %}

***

### Overview

The token issuer (which can be any subnet user) deploys a standard ERC20 contract once and registers it as *Bridgeable* on one Bitcoin-IPC subnet, called the *home subnet* of that token. From that point on, any user can bridge it to any other subnet with a single `transfer_erc()` call. The transfer is anchored on Bitcoin in the next checkpoint, and the destination subnet automatically deploys a wrapped version of the token on first arrival. A per-token balance ledger tracked on Bitcoin ensures no subnet can ever send out more tokens than it holds.

### Step 1: Deploy a Bridgeable ERC20 contract

Let's start by deploying an ERC20 contract on subnet A and by declaring it as *Bridgeable.* This can be done by calling a dedicated contract `registerBridgeableToken` , available by default in all Bitcoin-IPC subnets. We have provided a script that takes care of both steps. From inside the `bitcoin-ipc` repo, run the following:

```bash
# from inside the container
/workspace/ipc/scripts/deploy_bridgeable_token.sh \
	--name TOKEN1 --symbol TK1 --decimals 18 \
	--initial-supply 1000000000000000000000000 \
	--rpc-url $RPC_URL_1 \
	--private-key 21b16a87dd69bc6283045ab63738c9ab73c93c93f91e96cd0e54bd321bba80ad \
	--gateway 0x77aa40b105843728088c0132e43fc44348881da8 \
	--broadcast
```

**Arguments:**

* The `--rpc-url` points to Subnet 1.
* The `--private-key` corresponds to `IPC_ADDRESS_OF_VALIDATOR_1` and can be obtained by running `ipc-cli wallet export --address $IPC_ADDRESS_OF_VALIDATOR_1 --wallet-type btc`. This means we are using Validator 1 as the issuer of the token.&#x20;
* The `--gateway` indicates the contract (pre-deployed at every Bitcoin-IPC subnet) that contains the `registerBridgeableToken()` function.

The command returns the address of the deployed contract. Export it in the following variable:

```bash
export TOKEN_ADDR=<token address>
```

### Step 2: Query the token metadata

A dedicated `ipc-cli` command returns the metadata of a token.

```bash
ipc-cli --config-path /root/.ipc/config.toml \
    cross-msg query-token-metadata \
    --subnet $SUBNET_ID_1 \
    --home-subnet $SUBNET_ID_1 --home-token $TOKEN_ADDR
```

**Arguments:**

* The tuple `(home_subnet, home_token)` uniquely characterises each ERC20 contract deployed across the Bitcoin-IPC framework.
* The argument `--subnet` indicates the subnet were the query is sent.

**Output:**

The command returns the metadata (name, symbol, decimals) of the token  `(home_subnet, home_token)` and its address, and it looks as follows:

```bash
  "decimals": 18,
  "name": "TOKEN1",
  "symbol": "TK1",
  "wrapped_token": null
```

The `"wrapped_token": null` here is due to the fact that we have queried the **home subnet** of the token, and there is no wrapped representation of the token there.

### Step 3: Query the token metadata on Subnet B

Wait until Subnet 1 creates the next checkpoint. This will contain a Token Registration record, through which other subnets become aware of the token `(home_subnet, home_token)` .

We can then run the same command but direct it to Subnet 2 this time:

```bash
ipc-cli --config-path /root/.ipc/config.toml \
    cross-msg query-token-metadata \
    --subnet $SUBNET_ID_2 \
    --home-subnet $SUBNET_ID_1 --home-token $TOKEN_ADDR
```

**Output:**

```bash
{
  "decimals": 18,
  "name": "TOKEN1",
  "symbol": "TK1",
  "wrapped_token": null
}
```

Observe that a wrapped representation of our token does **not** yet exist in Subnet B. This is because the token has not been transferred into that subnet yet.

### Step 4: Transfer tokens&#x20;

At this point, the total supply of our token is in the issuer's account. Let's first transfer 10 tokens to User 1.

```bash
cast send $TOKEN_ADDR "transfer(address,uint256)" \
    $IPC_ADDRESS_OF_USER_1 10000000000000000000 \
    --rpc-url $RPC_URL_1 \
    --private-key 21b16a87dd69bc6283045ab63738c9ab73c93c93f91e96cd0e54bd321bba80ad
```

We can now execute a cross-chain transfer, sending 5 tokens from User 1 on Subnet 1 to User 2 on Subnet 2:

<pre class="language-bash"><code class="lang-bash"><strong>ipc-cli --config-path /root/.ipc/user1/config.toml cross-msg transfer-erc \
</strong>    --source-subnet $SUBNET_ID_1 \
    --destination-subnet $SUBNET_ID_2 \
    --source-address $IPC_ADDRESS_OF_USER_1 \
    --destination-address $IPC_ADDRESS_OF_USER_2 \
    --token $TOKEN_ADDR 5000000000000000000
</code></pre>

### Step 5: Read the balance of User 1 on Subnet 1

Wait until Subnet A creates a checkpoint and then read the balances of the two users:

```bash
cast call $TOKEN_ADDR "balanceOf(address)(uint256)" \
$IPC_ADDRESS_OF_USER_1 \
--rpc-url $RPC_URL_1
```

**Expected output:**

This should print `5000000000000000000 [5e18]` , which is 5 tokens printed with 18 demical zeros.

### Step 6: Query the token metadata on Subnet B again

Now Subnet 2 has received a valid incoming transfer for the token `(home_subnet, home_token)` , hence it automatically deployed a wrapped representation of it. Let's query the token's metadata again:

```bash
ipc-cli --config-path /root/.ipc/config.toml \
    cross-msg query-token-metadata \
    --subnet $SUBNET_ID_2 \
    --home-subnet $SUBNET_ID_1 --home-token $TOKEN_ADDR
```

**Output:**

```bash
  "decimals": 18,
  "name": "TOKEN1",
  "symbol": "TK1",
  "wrapped_token": "0xb6ffacc9643b95d353d7184b3ccc64471b53fdc9"
```

Observe now that the `wrapped_token` returns the address of the newly deployed wrapped token on Subnet B. Export the following variable:

```bash
export TOKEN_ADDR_SUBNET_2=<wrapped token address>
```

### Step 7: Read the balance of User 2 on Subnet 2

```bash
cast call $TOKEN_ADDR_SUBNET_2 "balanceOf(address)(uint256)" \
$IPC_ADDRESS_OF_USER_2 \
--rpc-url $RPC_URL_2
```

**Expected output:**

This should print `5000000000000000000 [5e18]` , which is 5 tokens printed with 18 decimal zeros.

### Step 7: Transfer tokens in the opposite directions

Let's try a transfer from Subnet 2 to Subnet 1 now, sending 4 tokens from User 2 to User 1.

```bash
ipc-cli --config-path /root/.ipc/user2/config.toml cross-msg transfer-erc \
    --source-subnet $SUBNET_ID_2 \
    --destination-subnet $SUBNET_ID_1 \
    --source-address $IPC_ADDRESS_OF_USER_2 \
    --destination-address $IPC_ADDRESS_OF_USER_1 \
    --token $TOKEN_ADDR_SUBNET_2 4000000000000000000
```

Wait for Subnet B to create a checkpoint. You can then use the commands from the previous step to query the balances. User 1 should have 9 tokens on Subnet 1, and User 2 should have 1 token on Subnet 2.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bitcoin-scaling-labs-docs.gitbook.io/ipc-btc-scaling-docs/user-guide-for-using-subnets/cross-chain-transfers-of-custom-tokens.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
