Motif Node is the core infrastructure component of the Motif Protocol — a decentralized platform enabling Bitcoin liquidity providers to create DTPs. It facilitates the seamless locking of Bitcoin through Bitcoin Pods, which is secured by EigenLayer shared services.
The node is responsible for:
- Managing Bitcoin deposits and their representation on Ethereum.
- Interacting with EigenLayer for shared security.
- Supporting vault strategies that generate yield through integrated DeFi protocols.
This repository contains the core logic for running a Motif Node, including smart contract interactions, on-chain operations, and node management tools.
The architecture includes the following components:
- Event Manager: A thread which registers to the events of
Motif
AVS contract. - Deposit Checker: A thread which confirms the BTC deposit form a BTC node .
- Api Server: An API server to create new multisig addresses and PSBTs.
- bitcoind Offline Wallet: An offline server that hosts a single wallet containing a signer BTC key. This server is used to sign PSBT transactions forwarded by the
API server
. - Signet Node: A
BTC
fullSignet
node with txindexing enabled, used to watch addresses, create transactions and broadcast them.
For a production environment, it is highly recommended to deploy the bitcoind Offline Wallet and the nyks Full Node/btc-oracle on separate hosts.
The btc-oracle offline signer design is based on remote signing available in bitcoind
and lnd
.
Signer mode, however, does not require a BTC full node connection as the signer is not responsible for creating or broadcasting transactions.
References:
We recommend using an instance of [bitcoin-core] (https://bitcoin.org/en/releases/27.0/) configured in the Offline signing wallet mode.The Bitcoin Core wallet is the preferred choice because it enables clients to utilize external signers and boasts a long-standing, rigorously tested codebase.
For ensuring wallet security, it is strongly recommended to use a separate host system with atleast 4 GB RAM and 2 GB available storage space. The system should be completely disconnected from all public networks (internet, tor, wifi etc.). The offline
wallet host is not required to download or synchronize blockchain data.
The offline wallet should be setup as a server as part of a secured private network where, the wallet is only accessible through a designated rpc connection with btc-oracle
. To ensure the integrity and security of the data, only TLS
based rpc connection should be allowed between btc-oracle
and the offline wallet node.
Download and install the bitcoin binaries according to your operating systemfrom the official Bitcoind Core registry. All programs in this guide are compatible with version 27.0
.
bitcoind
instance can be configured by using a bitcoin.conf
file. In Linux
based systems the file is found in /home/<username>/.bitcoin
.
A sample configuration file with recommended settings is as follows
# Accept command line and JSON-RPC commands
server=1
# RPC server settings
rpcuser=<rpc-username>
rpcpassword=<rpc-password>
# field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>.
# rpcauth = <userpw>
# Port your bitcoin node will listen for incoming requests;
# listening for bitcoin mainnet
rpcport=8332
# Address your bitcoin node will listen for incoming requests
# should be the address of your offline host
rpcbind=0.0.0.0
# Needed for remote node connectivity
# btc-oracle IP should only be allowed
rpcallowip=0.0.0.0/0
# Offline Wallet server shouldn't connect to any external p2p or chain node
connect=0
JSON-RPC connection authentication can be configured to use rpc-username
:rpc-password
pair or a username and HMAC-SHA-256 hashed password
through rpcauth option. It is not recommended to hardcode rpc-password
in the config file. The salted hash can be created from canonical python script included in the share/rpcauth in bitcoin-core installed directory.
By default, the bitcoind
server can be run using the following command.
bitcoind
In case, a non-default home directory was used during installation:
bitcoind -datadir=/path/to/bitcoin/home
The following commands shall be used to create and manage BTC wallet on the offline host. The wallet will contain a single address controlled by a private key that will be used to sign transactions recieved from btc-oracle
-
Create the wallet
bitcoin-cli -named createwallet \ wallet_name=<wallet_name> \ passphrase=<passphrase> \ load_on_startup=true \
Flags explanation:
wallet_name
: The name of the walletpassphrase
: The passphrase that will be used to encrypt thewallet.dat
file.load_on_startup=true
: Ensures that the wallet is automatically loaded in case ofbitcoind
server restart
-
Create a new address
bitcoin-cli getnewaddress
Save the address for future use. This address shall be used to recieve/send funds on the BTC chain.
-
Obtain 33-byte BTC public key derived from the above address
bitcoin-cli getaddressinfo <btc_address> | jq -r .pubkey
Maintain a record of the public key in its hexadecimal string fromat. The btc public key will be used during the signer registration process when setting up the
btc-oracle
to act as FragmentSigner
. -
The wallet can be unlocked on the offline host using the following command
bitcoin-cli walletpassphrase <passphrase> <unlock_time>
where:
passphrase
is the same as when used for creating the wallet and,unlock_time
is the amount of time the wallet is unlocked for in seconds
-
The wallet can be backedup and restored
# Backup the wallet bitcoin-cli -rpcwallet=<wallet-name> backupwallet /path/to/backup/wallet.dat # Restore the wallet bitcoin-cli restorewallet <wallet-name> /path/to/backup/wallet.dat
It is recommended to take periodic backups of the wallet to keep it secure.
The config/config.json file contains various configuration settings required for connecting to Bitcoin and Ethereum nodes, as well as other related services. Below is a detailed explanation of each configuration parameter:
btc_node_pass
: The password for accessing the Bitcoin node.btc_node_protocol
: The protocol used to connect to the Bitcoin node (e.g., http://).fee_rate_adjustment
: The adjustment rate for transaction fees. Increses the btc tx fee per byte (in sats)wallet_name
: The name of the Bitcoin wallet used for transactions.btc_node_host
: The host URL for connecting to the Bitcoin node.btc_node_user
: The username for accessing the Bitcoin node.btc_xpublic_key
: The extended public key for the Bitcoin wallet, used for generating addresses.multisig_signing_wallet_name
: Offline wallet name,multisig_btc_node
: Offline btc node,multisig_btc_user
: "Offline Btc user",multisig_btc_pass
: "Offline Btc Pass",multisig_btc_protocol
: "Offline Btc protocol,
eth_rpc_host
: The RPC host URL for connecting to the Ethereum node.eth_ws_host
: The WebSocket host URL for connecting to the Ethereum node.eth_keystore_dir
: The directory where the Ethereum keystore files are stored.eth_keystore_passphrase
: The password for accessing the Ethereum keystore.
opr_metadata_uri
: The URI for the OPR metadata (currently empty).eigen_delegation_manager_address
: The Ethereum address of the Eigen Delegation Manager contract.motif_registry_address
: The Ethereum address of the Motif Registry contract.service_manager_address
: The Ethereum address of the Service Manager contract.eigen_avs_directory_address
: The Ethereum address of the Eigen AVS Directory contract.pod_manager_address
: The Ethereum address of the Pod Manager contract
DB_port
: The port number for the database connection.DB_host
: The host URL for the database connection.DB_user
: The username for the database connection.DB_password
: The password for the database connection.DB_name
: The Name of the database.
This configuration file should be updated with the appropriate values for your specific setup. Ensure that sensitive information such as passwords and private keys are securely managed.
The contract addresses for the deployed contracts are available on the Motif repo.
The System Uses the go-ethereum library's keystore save keys. if there is no keystore file in the directory (mentioned in config file) the system will generate a new key and save it in a newly created keystore located at the mentioned directory encrypted with the password mentioned in config file.
If you have a Private key you want to use specifically, you can use the code snippet below to create a keystore before you run Motif Operator.
package main
import (
"encoding/hex"
"fmt"
"log"
"os"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
)
func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: go run main.go <private_key_hex> <password>")
return
}
privateKeyHex := os.Args[1]
password := os.Args[2]
// Decode the private key from hex
privateKeyBytes, err := hex.DecodeString(privateKeyHex)
if err != nil {
log.Fatalf("Failed to decode private key: %v", err)
}
// Convert the private key bytes to an ECDSA private key
privateKey, err := crypto.ToECDSA(privateKeyBytes)
if err != nil {
log.Fatalf("Failed to convert private key: %v", err)
}
// Create a keystore
ks := keystore.NewKeyStore("keystore", keystore.StandardScryptN, keystore.StandardScryptP)
// Create a new account with the private key
account, err := ks.ImportECDSA(privateKey, password)
if err != nil {
log.Fatalf("Failed to import private key to keystore: %v", err)
}
fmt.Printf("Keystore created: %s\n", account.Address.Hex())
}
To run the operator, you will first have to install Golang
To run the operator run the following commands from root
go mod tidy
go build .
go run main.go
This will start all the components of the operator. It is ideal to run it inside a process manager like systemd
or tmux
to ensure that the operator is running continuously.
The API server is used to create new multisig addresses and PSBTs. It is a simple HTTP server that listens on port 8080.
Endpoint
: /eigen/get_addressMethod
: POSTDescription
: It is a POST operation that returns a new multisig generated using the provided pubkey and the pubkey of the operator.Body
:
{
"pubKey":"",
}
Endpoint
: /eigen/bech32_to_hex ans /eigen/hex_to_bech32Method
: POSTDescription
: It is an endpoint used to convert bech32 to a hex string and vice versaBody
:
{
"address":"",
}
Endpoint
: /eigen/nodeMethod
: GETDescription
: Retrieves information about the node.
Endpoint
: /eigen/node/healthMethod
: GETDescription
: Checks the health status of the node.
Endpoint
: /eigen/node/servicesMethod
: GETDescription
: Retrieves a list of services running on the node.
Endpoint
: /eigen/node/services/{service_ID}/healthMethod
: GETDescription
: Checks the health status of a specific service identified by service_ID.
We use Postgres DB. Once the DB is installed you can run the .db/schema.sql file to create the tables.