> For the complete documentation index, see [llms.txt](https://bitcoin-scaling-labs-docs.gitbook.io/ipc-btc-scaling-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bitcoin-scaling-labs-docs.gitbook.io/ipc-btc-scaling-docs/user-guide-for-using-subnets/cross-chain-transfers-of-custom-tokens.md).

# 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/cross-chain.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.
