For the complete documentation index, see llms.txt. This page is also available as Markdown.

Deploying a new fee collector

Deploy your own fee collector contract and configure its fee parameters.

To start receiving fees from swaps you will need to deploy the fee collector contract instance using the Provider Swap Fee Factory:

Provider Swap Fee Factory address is CA4Q2T6FRAFYJYSMDJV7F6B7RL5PS6QS2UOZHBMCT2KSMGQRAAKP2MKO

  • Function: deploy_swap_fee_contract(operator: Address, fee_destination: Address, max_swap_fee_fraction: u32, swap_fee_fraction_denominator: u32) -> Address

  • Returns: Address of the newly deployed ProviderSwapFeeCollector.

Parameters explained:

  • operator- an address that will be able to claim fees

  • fee_destination - an address that will be able to receive the claimed fees.

  • max_swap_fee_fraction - maximum value of a swap fee that a given fee collector instance can accept.

  • swap_fee_fraction_denominator - basis points denominator. 10000 works well as a starting point; however, it can be increased for extended fee resolution.

Notes:

  • operator and fee_destination addresses can be the same but we recommend using different addresses for greater security.

  • fee_destination must have all the trustlines in case the operator withdraws "raw" fees accumulated in various currencies. Aquarius has a mechanism to settle fees in one single currency though.

  • when executing swaps with fees you can set a dynamic fee, as long as it does not exceed the max_swap_fee_fraction parameter.

import binascii

from stellar_sdk import Keypair, Network, scval, Server, SorobanServer, StrKey, TransactionBuilder, xdr


def main():
    # =========================================
    # Configuration & Setup
    # =========================================
    # Operator account to be assigned as the fee contract operator
    operator_secret_key = "SA..........."
    operator_keypair = Keypair.from_secret(operator_secret_key)

    # Address to receive the swap fees
    fee_destination_address = "GC..........."

    # Factory contract for provider swap fee contracts (already deployed on network)
    provider_fee_factory_contract_id = "CA4Q2T6FRAFYJYSMDJV7F6B7RL5PS6QS2UOZHBMCT2KSMGQRAAKP2MKO"

    # Maximum allowed swap fee in basis points
    #   (depends on denominator, e.g. 100 bps with 10000 denominator = 1% fee)
    max_swap_fee_bps = 100    # 100 bps = 1% = 100 / 10000
    swap_fee_bps_denominator = 10000

    # Soroban and Horizon server endpoints (adjust for your network)
    soroban_server = SorobanServer("https://mainnet.sorobanrpc.com")
    horizon_server = Server("https://horizon.stellar.org/")
    network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE

    # =========================================
    # Build Transaction to Deploy Provider Swap Fee Contract
    # =========================================
    # This transaction calls:
    #   deploy_swap_fee_contract(
    #       operator: Address,
    #       fee_destination: Address,
    #       swap_fee_bps: u32,
    #       swap_fee_fraction_denominator: u32,
    #   )
    # on the provider fee factory contract; it returns the address of the newly deployed fee contract.

    # Load operator account from Horizon (must have sufficient XLM)
    source_account = horizon_server.load_account(operator_keypair.public_key)

    # Build transaction
    tx = (
        TransactionBuilder(
            source_account=source_account,
            network_passphrase=network_passphrase,
            base_fee=10000
        )
        .set_timeout(300)
        .append_invoke_contract_function_op(
            contract_id=provider_fee_factory_contract_id,
            function_name="deploy_swap_fee_contract",
            parameters=[
                # operator address (as an SCVal address)
                scval.to_address(operator_keypair.public_key),
                # fee destination address (as an SCVal address)
                scval.to_address(fee_destination_address),
                # max swap fee in basis points (as a uint32)
                scval.to_uint32(max_swap_fee_bps),
                # swap fee bps denominator (as a uint32)
                scval.to_uint32(swap_fee_bps_denominator),
            ]
        )
        .build()
    )

    prepared_tx = soroban_server.prepare_transaction(tx)

    # Sign the transaction with the operator keypair
    prepared_tx.sign(operator_keypair)

    print("Submitting transaction to Horizon...")
    horizon_response = horizon_server.submit_transaction(prepared_tx)
    if not horizon_response['successful']:
        print("Transaction submission failed.")
        return

    response = soroban_server.get_transaction(horizon_response['hash'])
    if not response.result_meta_xdr:
        print("No meta in response.")
        return

    meta = xdr.TransactionMeta.from_xdr(response.result_meta_xdr)
    if meta.v3 and (meta.v4 if meta.v == 4 else meta.v3).soroban_meta:
        address = StrKey.encode_contract(
            binascii.unhexlify((meta.v4 if meta.v == 4 else meta.v3).soroban_meta.return_value.address.contract_id.hash.hex()))
        print("Deploy successful!")
        print(f"Deployed address: {address}")
    else:
        print("No result returned by the contract.")


if __name__ == "__main__":
    main()

Last updated