Fiber LogoFiber Docs

Basic Transfer Example

Learn how to set up and execute transfers between two nodes

Requirements
Updated 4/9/2026
latest

TL;DR

Set up two local nodes, open a channel between them, and send a CKB payment from one to the other. The whole process takes about 10 minutes.

Overview

This guide walks you through setting up and executing a basic token (CKB) transfer between two nodes on the Fiber Testnet.

Prerequisites

  • Git (if building from source)
  • Rust and Cargo (if building from source)
  • Basic understanding of command line operations
  • curl for making RPC calls
  • ckb-cli for generating keys

Setting Up Your Nodes

1. Prepare Fiber Binary

Download the latest release binary from the Fiber GitHub Releases page, or build from source:

git clone https://github.com/nervosnetwork/fiber.git
cd fiber
cargo build --release

macOS Security

If you're using macOS, remove the quarantine attribute:

xattr -d com.apple.quarantine fnn fnn-cli

HTTP Proxy Issues

If you encounter 503 errors when using fnn-cli, run:

export NO_PROXY=127.0.0.1,localhost

2. Create Data Directories

# For Node 1
mkdir node1
cp target/release/fnn node1/
cp target/release/fnn-cli node1/
cp config/testnet/config.yml node1/

# For Node 2
mkdir node2
cp target/release/fnn node2/
cp target/release/fnn-cli node2/
cp config/testnet/config.yml node2/

3. Configure Node Keys

Each node needs its own private key. Create two separate CKB accounts:

ckb-cli account new  # for Node 1
ckb-cli account new  # for Node 2

Export the keys:

# In node1 directory
mkdir ckb
ckb-cli account export --lock-arg <node1_lock_arg> --extended-privkey-path ./ckb/exported-key
head -n 1 ./ckb/exported-key > ./ckb/key

# In node2 directory
mkdir ckb
ckb-cli account export --lock-arg <node2_lock_arg> --extended-privkey-path ./ckb/exported-key
head -n 1 ./ckb/exported-key > ./ckb/key

Key File Format

The ckb/key file must contain only the 64-character hex private key (first line), without 0x prefix.

Get Testnet funds from https://faucet.nervos.org for both nodes.

4. Configure Ports

Edit config.yml for each node:

  • Node 1: RPC Port 8227, P2P Port 8228
  • Node 2: RPC Port 8237, P2P Port 8238
View complete config.yml example
fiber:
  listening_addr: "/ip4/127.0.0.1/tcp/8228"
  bootnode_addrs:
    - "/ip4/54.179.226.154/tcp/8228/p2p/Qmes1EBD4yNo9Ywkfe6eRw9tG1nVNGLDmMud1xJMsoYFKy"
    - "/ip4/54.179.226.154/tcp/18228/p2p/QmdyQWjPtbK4NWWsvy8s69NGJaQULwgeQDT5ZpNDrTNaeV"
  announce_listening_addr: true
  chain: testnet
  scripts:
    - name: FundingLock
      script:
        code_hash: 0x6c67887fe201ee0c7853f1682c0b77c0e6214044c156c7558269390a8afa6d7c
        hash_type: type
        args: 0x
      cell_deps:
        - type_id:
            code_hash: 0x00000000000000000000000000000000000000000000000000545950455f4944
            hash_type: type
            args: 0x3cb7c0304fe53f75bb5727e2484d0beae4bd99d979813c6fc97c3cca569f10f6
        - cell_dep:
            out_point:
              tx_hash: 0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7
              index: 0x0
            dep_type: code
    - name: CommitmentLock
      script:
        code_hash: 0x740dee83f87c6f309824d8fd3fbdd3c8380ee6fc9acc90b1a748438afcdf81d8
        hash_type: type
        args: 0x
      cell_deps:
        - type_id:
            code_hash: 0x00000000000000000000000000000000000000000000000000545950455f4944
            hash_type: type
            args: 0xf7e458887495cf70dd30d1543cad47dc1dfe9d874177bf19291e4db478d5751b
        - cell_dep:
            out_point:
              tx_hash: 0x12c569a258dd9c5bd99f632bb8314b1263b90921ba31496467580d6b79dd14a7
              index: 0x0
            dep_type: code
rpc:
  listening_addr: "127.0.0.1:8227"
ckb:
  rpc_url: "https://testnet.ckbapp.dev/"
  udt_whitelist:
    - name: RUSD
      script:
        code_hash: 0x1142755a044bf2ee358cba9f2da187ce928c91cd4dc8692ded0337efa677d21a
        hash_type: type
        args: 0x878fcc6f1f08d48e87bb1c3b3d5083f23f8a39c5d5c764f253b55b998526439b
      cell_deps:
        - type_id:
            code_hash: 0x00000000000000000000000000000000000000000000000000545950455f4944
            hash_type: type
            args: 0x97d30b723c0b2c66e9cb8d4d0df4ab5d7222cbb00d4a9a2055ce2e5d7f0d8b0f
      auto_accept_amount: 1000000000
services:
  - fiber
  - rpc
  - ckb

Step-by-Step Transfer Process

1. Start Both Nodes

# Terminal 1 — Node 1
cd node1
FIBER_SECRET_KEY_PASSWORD='password1' RUST_LOG=info ./fnn -c config.yml -d .

# Terminal 2 — Node 2
cd node2
FIBER_SECRET_KEY_PASSWORD='password2' RUST_LOG=info ./fnn -c config.yml -d .

2. Connect the Nodes

First get Node 2's pubkey, then connect from Node 1:

# Get Node 2's pubkey
cd node2 && ./fnn-cli --url http://127.0.0.1:8237 info | grep pubkey

# Connect from Node 1
cd node1 && ./fnn-cli peer connect_peer --pubkey <node2_pubkey> --address "/ip4/127.0.0.1/tcp/8238"
# Get Node 2's pubkey
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"id":"42","jsonrpc":"2.0","method":"node_info"}' \
  http://localhost:8237 | grep pubkey

# Connect from Node 1
curl -s -X POST -H "Content-Type: application/json" \
  -d '{
    "id": "42", "jsonrpc": "2.0", "method": "connect_peer",
    "params": [{"pubkey": "<node2_pubkey>", "address": "/ip4/127.0.0.1/tcp/8238"}]
  }' http://localhost:8227
CCC support coming soon.

3. Open a Payment Channel

# funding-amount is in shannons (50000000000 = 500 CKB)
cd node1 && ./fnn-cli channel open_channel \
  --pubkey <node2_pubkey> \
  --funding-amount 50000000000 \
  --public true
# funding_amount in hex (0xba43b7400 = 500 CKB)
curl -s -X POST -H "Content-Type: application/json" \
  -d '{
    "id": "42", "jsonrpc": "2.0", "method": "open_channel",
    "params": [{"pubkey": "<node2_pubkey>", "funding_amount": "0xba43b7400", "public": true}]
  }' http://localhost:8227
CCC support coming soon.

Check channel status — wait until state_name becomes CHANNEL_READY:

./fnn-cli channel list_channels
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"id":"42","jsonrpc":"2.0","method":"list_channels","params":[{}]}' \
  http://localhost:8227
CCC support coming soon.

4. Generate an Invoice

Create a payment invoice on Node 2 for 100 CKB:

# amount in shannons (10000000000 = 100 CKB)
cd node2 && ./fnn-cli --url http://127.0.0.1:8237 invoice new_invoice \
  --amount 10000000000 \
  --currency Fibt \
  --description "test invoice"
# Generate a random payment_preimage first
payment_preimage="0x$(openssl rand -hex 32)"

curl -s -X POST -H "Content-Type: application/json" \
  -d "{
    \"id\": \"42\", \"jsonrpc\": \"2.0\", \"method\": \"new_invoice\",
    \"params\": [{
      \"amount\": \"0x2540be400\",
      \"currency\": \"Fibt\",
      \"description\": \"test invoice\",
      \"expiry\": \"0xe10\",
      \"payment_preimage\": \"$payment_preimage\",
      \"hash_algorithm\": \"sha256\"
    }]
  }" http://localhost:8237
CCC support coming soon.

5. Make the Payment

cd node1 && ./fnn-cli payment send_payment --invoice "fibt100000000001p..."
curl -s -X POST -H "Content-Type: application/json" \
  -d '{
    "id": "42", "jsonrpc": "2.0", "method": "send_payment",
    "params": [{"invoice": "fibt100000000001p..."}]
  }' http://localhost:8227
CCC support coming soon.

6. Verify the Transfer

Check channel balances to confirm the transfer:

# Node 1
./fnn-cli channel list_channels

# Node 2
./fnn-cli --url http://127.0.0.1:8237 channel list_channels
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"id":"42","jsonrpc":"2.0","method":"list_channels","params":[{}]}' \
  http://localhost:8227
CCC support coming soon.

After sending 100 CKB from Node 1, Node 1's local_balance decreases by ~100 CKB and remote_balance increases by 100 CKB.

Closing the Channel

cd node1 && ./fnn-cli channel shutdown_channel \
  --channel-id <channel_id> \
  --force true
curl -s -X POST -H "Content-Type: application/json" \
  -d '{
    "id": "42", "jsonrpc": "2.0", "method": "shutdown_channel",
    "params": [{
      "channel_id": "<channel_id>",
      "close_script": {
        "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8",
        "hash_type": "type",
        "args": "<your_lock_arg>"
      },
      "fee_rate": "0x3FC"
    }]
  }' http://localhost:8227
CCC support coming soon.

CLI vs RPC for closing

With CLI --force true, close_script and fee_rate are determined automatically. With RPC, you must provide close_script matching your node's CKB lock script (get it from ckb-cli account list).

Next Steps

Fiber AI Assistant

Ask me anything

I can answer questions about Fiber Network using our documentation.

AI answers are based on Fiber documentation