A comprehensive Python tool for interacting with Linkage Finance smart contracts on Cardano. This tool enables developers to create funds, manage deposits and withdrawals, collect royalties, and perform other operations directly with the Linkage Finance protocol without depending on the Linkage Finance web interface.
Linkage Finance is a decentralized protocol on Cardano that allows users to create index funds (baskets of tokens) with automatic rebalancing and royalty collection. Users can deposit tokens into funds and receive fund tokens representing their share, while fund creators earn royalties from trading activity.
- Fund Creation: Create custom index funds with your own token baskets and weights
- Deposits & Withdrawals: Easily deposit tokens into funds and withdraw your share
- Royalty Collection: Fund creators can collect accumulated royalties
- Multiple Networks: Support for Mainnet, Preprod, and local Devnet
- Wallet Integration: Multiple wallet loading options (mnemonic, keys, files)
- Error Handling: Comprehensive error handling and transaction result reporting
- Type Safety: Full type annotations for better development experience
- Python 3.8 or higher
- Cardano node access (via Blockfrost for mainnet/preprod or local node for devnet)
- Smart contract CBOR files (provided by Linkage Finance team)
- Clone this repository:
git clone https://github.com/linkage-finance/linkage-python-tool.git
cd linkage-python-tool- Install dependencies:
pip install -r requirements.txt- Install the package:
pip install -e .pip install linkage-finance-toolContact the Linkage Finance team to obtain the smart contract CBOR files. Create a contracts directory and organize them like this:
contracts/
├── free_mint/
│ └── script.cbor
├── linkage_authenticator_compressed/
│ └── script.cbor
├── linkage_factory_compressed/
│ └── script.cbor
└── linkage_authenticated_compressed/
└── script.cbor
- Go to blockfrost.io
- Create an account
- Create a new project
- Copy your API key
You can use the tool with:
- Mnemonic phrase (12-24 words)
- Signing key (hex format)
- Key files (cardano-cli format)
- Generate new wallet (tool will create one for you)
Here's a simple example to get you started:
from linkage_tool import LinkageClient, NetworkConfig
# 1. Initialize client
client = LinkageClient(
network=NetworkConfig.PREPROD,
blockfrost_api_key="your_api_key_here",
contracts_dir="./contracts"
)
# 2. Load your wallet
wallet = client.load_wallet_from_mnemonic(
"your twelve word mnemonic phrase here for the wallet access"
)
print(f"Wallet address: {client.wallet_address}")
# 3. Set reference script address (provided by Linkage team)
client.set_reference_script_address("addr_test1qz...")
# 4. Create factory (one-time setup)
result = client.create_factory()
if result.success:
print(f"Factory created! TX: {result.transaction_id}")
# 5. Mint test tokens
result = client.mint_test_tokens(num_tokens=5)
if result.success:
print(f"Tokens minted! TX: {result.transaction_id}")
# 6. Create a fund
tokens = ["policy_id_hex" + "token1".encode().hex()] # Your token list
result = client.create_fund(
fund_name="MyFirstFund",
tokens=tokens,
factors=[1000], # Token weights
royalty_factor=3 # 3% royalty
)
if result.success:
print(f"Fund created! TX: {result.transaction_id}")from linkage_tool import LinkageClient, NetworkConfig
# For Preprod testnet (recommended for testing)
client = LinkageClient(
network=NetworkConfig.PREPROD,
blockfrost_api_key="preprod_api_key",
contracts_dir="./contracts"
)
# For Mainnet (production)
client = LinkageClient(
network=NetworkConfig.MAINNET,
blockfrost_api_key="mainnet_api_key",
contracts_dir="./contracts"
)
# For local devnet (development)
client = LinkageClient(
network=NetworkConfig.DEVNET,
contracts_dir="./contracts"
)wallet = client.load_wallet_from_mnemonic(
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
)wallet = client.load_wallet_from_keys("your_signing_key_in_hex_format")wallet = client.load_wallet_from_files(
skey_path="path/to/wallet.skey",
vkey_path="path/to/wallet.vkey" # optional
)wallet, mnemonic = client.generate_new_wallet()
print(f"New wallet created: {wallet}")
print(f"Mnemonic (save this!): {mnemonic}")# Define your token basket
tokens = [
"policy1_hex" + "btc".encode().hex(),
"policy1_hex" + "eth".encode().hex(),
"policy1_hex" + "ada".encode().hex(),
]
# Define token weights/factors
factors = [1000, 800, 2000] # BTC: 1000, ETH: 800, ADA: 2000
# Create the fund
result = client.create_fund(
fund_name="CryptoBasket",
tokens=tokens,
factors=factors,
fund_token_factor=1000, # Base factor for fund tokens
royalty_factor=3 # 3% royalty on deposits
)
if result.success:
print(f"Fund created successfully!")
print(f"Transaction ID: {result.transaction_id}")
fund_tx_id = result.transaction_id
else:
print(f"Fund creation failed: {result.error_message}")result = client.deposit_to_fund(
fund_tx_hash=fund_tx_id, # TX hash from fund creation
deposit_multiple=1000 # Deposit 1000x the minimum amount
)
if result.success:
print(f"Deposit successful! TX: {result.transaction_id}")
new_fund_tx_id = result.transaction_id # Fund UTXO moves to new TXresult = client.withdraw_from_fund(
fund_tx_hash=current_fund_tx_id,
withdraw_multiple=500 # Withdraw 500x the minimum amount
)
if result.success:
print(f"Withdrawal successful! TX: {result.transaction_id}")# Check current balance first
balance = client.get_fund_balance(current_fund_tx_id)
if balance and balance.accrued_royalty > 0:
result = client.collect_royalties(
fund_tx_hash=current_fund_tx_id,
collect_amount=balance.accrued_royalty # Collect all royalties
)
if result.success:
print(f"Royalties collected! TX: {result.transaction_id}")# Get fund balance and state
balance = client.get_fund_balance(fund_tx_id)
if balance:
print(f"Fund tokens: {balance.fund_tokens}")
print(f"Accrued royalties: {balance.accrued_royalty}")
print(f"Underlying tokens: {balance.underlying_tokens}")All operations return a TransactionResult object:
result = client.create_fund(...)
if result.success:
print(f"Success! TX: {result.transaction_id}")
print(f"Fee paid: {result.fee} lovelace")
else:
print(f"Failed: {result.error_message}")# Create a fund with custom configuration
result = client.create_fund(
fund_name="AdvancedFund",
tokens=your_token_list,
factors=your_custom_factors,
fund_token_factor=2000, # Custom fund token factor
royalty_factor=5 # 5% royalty instead of default 3%
)info = client.get_network_info()
print(f"Network: {info['network']}")
print(f"Wallet: {info['wallet_address']}")
print(f"Reference scripts: {info['ref_script_address']}")# Get the address of any contract
factory_addr = client.get_contract_address("linkage_factory", compressed=True)
fund_addr = client.get_contract_address("linkage_authenticated", compressed=True)See examples/basic_usage.py for a complete step-by-step example covering:
- Setting up the client
- Creating a factory
- Minting test tokens
- Creating and managing funds
See examples/advanced_usage.py for advanced patterns including:
- Managing multiple funds
- Batch operations
- Error handling strategies
- Portfolio monitoring
- Automatic royalty collection
- Use PREPROD for testing and development
- Only use MAINNET for production applications
- Be aware of transaction fees and network congestion
- Make sure you've set the correct reference script address
- Ensure the smart contracts have been deployed to that address
- Check that you're on the correct network
- Ensure your wallet has enough ADA for transaction fees
- Check that you have the required tokens for deposits
- Verify minimum UTXO requirements are met
- Verify your API key is correct and active
- Check you haven't exceeded rate limits
- Ensure you're using the right API key for your network
We welcome contributions! Please see our contributing guidelines for:
- Code style requirements
- Testing procedures
- Pull request process
- Bug reporting