Class TransactionBuilder

    TransactionBuilder - Fluent API for building Klever blockchain transactions

    Supports three build modes:

    1. buildRequest() - Create request object for node endpoint
    2. buildProto(options) - Build proto offline (client-side)
    3. build() - Build using node endpoint (requires provider)
    // Chainable building and signing
    const tx = await TransactionBuilder.create(provider)
    .sender('klv1...')
    .transfer({ receiver: 'klv1...', amount: '1000000' })
    .build()

    await tx.sign(privateKey)
    const hash = await provider.sendRawTransaction(tx.toHex())

    // Node-assisted building
    const provider = new KleverProvider({ network: 'mainnet' })
    const tx = await new TransactionBuilder(provider)
    .sender('klv1...')
    .transfer({ receiver: 'klv1...', amount: '1000000' })
    .build()

    // Offline building
    const tx = new TransactionBuilder()
    .transfer({ receiver: 'klv1...', amount: '1000000' })
    .buildProto({
    sender: 'klv1...',
    nonce: 123,
    fees: { kAppFee: 500000, bandwidthFee: 100000 }
    })

    Constructors

    Methods

    • Add a contract using ContractRequestData Routes to the appropriate builder method based on contractType. This is a generic method that automatically calls the correct specialized method.

      Contract Types:

      • 0: Transfer
      • 1: CreateAsset
      • 2: CreateValidator
      • 4: Freeze
      • 5: Unfreeze
      • 6: Delegate
      • 7: Undelegate
      • 8: Withdraw
      • 9: Claim
      • 14: Vote
      • 63: SmartContract

      Parameters

      • contract: ContractRequestData

        Contract request data with contractType

      Returns this

      This builder instance for chaining

      // Add transfer contract directly
      builder.addContract({
      contractType: 0,
      receiver: 'klv1...',
      amount: 1000000
      })

      // Add freeze contract
      builder.addContract({
      contractType: 4,
      amount: 5000000,
      kda: 'KLV'
      })
    • Build transaction using node endpoint (requires provider) This is the recommended method for most use cases as the node handles complex operations.

      Node-Assisted Building: The node automatically handles:

      • Nonce fetching (gets current account nonce)
      • Fee calculation (computes optimal kAppFee and bandwidthFee)
      • Proto encoding (creates valid proto bytes)
      • Validation (ensures transaction is valid)

      When to Use:

      • Standard wallet applications
      • When you have internet connectivity
      • When you want automatic fee calculation
      • When you don't need to control every parameter

      Comparison with buildProto():

      • build() = Online, automatic, easy (requires provider)
      • buildProto() = Offline, manual, flexible (no network needed)

      Returns Promise<Transaction>

      Transaction object with proto bytes from node, ready to sign

      If provider is not set or no contracts added

      If node response is invalid or network request fails

      // Simple node-assisted build
      const provider = new KleverProvider({ network: 'mainnet' })
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1xyz...')
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .build()

      // Node automatically fetches nonce and calculates fees
      await tx.sign(privateKey)
      const hash = await provider.sendRawTransaction(tx.toHex())

      // Build with multiple contracts
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1xyz...')
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .freeze({ amount: '5000000' })
      .delegate({ receiver: 'klv1validator...' })
      .build()

      // Override specific parameters
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1xyz...')
      .nonce(150) // Override automatic nonce
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .build()
    • Build proto transaction offline (client-side, no network required) This method creates a transaction entirely on the client side without contacting the node. You must provide all required parameters (sender, nonce, fees) either via builder state or options.

      Offline Mode Benefits:

      • No network latency
      • Works without internet connection
      • Full control over transaction parameters
      • Ideal for hardware wallets and air-gapped signing

      Fee Calculation: When building offline, fees must be provided manually or estimated:

      • KAppFee: Base fee for the contract type (typically 500000-1000000)
      • BandwidthFee: Fee based on transaction size (typically 100000-500000)
      • KDAFee: Optional - pay fees in custom KDA instead of KLV

      Parameters

      • options: BuildCallOptions = {}

        Build options (sender, nonce, fees, etc.)

        Build call options All fields are optional - will use builder's state if not provided

        • OptionalchainId?: string

          Chain ID for offline transaction building

        • Optionaldata?: string[]

          Transaction data (for smart contract calls)

        • Optionalfees?: { bandwidthFee: number; kAppFee: number }

          Fees for offline transaction building

        • OptionalkdaFee?: { amount: AmountLike; kda: string }

          KDA fee for offline transaction building

        • Optionalnonce?: number

          Nonce for offline transaction building

        • OptionalpermissionId?: number

          Permission ID for the transaction

        • Optionalsender?: string

          Sender address

        • Optionalvalue?: Record<string, bigint>

          Value to send with transaction (e.g., { KLV: parseKLV('1') })

      Returns Transaction

      Transaction object with proto bytes ready to sign

      If required parameters are missing or invalid

      // Offline build with all parameters in options
      const tx = TransactionBuilder.create()
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .buildProto({
      sender: 'klv1xyz...',
      nonce: 123,
      chainId: '100',
      fees: {
      kAppFee: 500000,
      bandwidthFee: 100000
      }
      })

      await tx.sign(privateKey)
      const hex = tx.toHex()

      // Offline build using builder state
      const tx = TransactionBuilder.create()
      .sender('klv1xyz...')
      .nonce(123)
      .setChainId('100')
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .buildProto({
      fees: { kAppFee: 500000, bandwidthFee: 100000 }
      })

      // Offline build with KDA fee (pay fees in custom token)
      const tx = TransactionBuilder.create()
      .sender('klv1xyz...')
      .nonce(123)
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .buildProto({
      chainId: '100',
      fees: { kAppFee: 0, bandwidthFee: 0 },
      kdaFee: { kda: 'MYTOKEN-ABCD', amount: '1000000' }
      })
    • Build transaction request object for node endpoint Creates a request object that can be sent to the node's /transaction/build endpoint The node will handle nonce fetching, fee calculation, and proto encoding

      Returns BuildTransactionRequest

      Request object ready to send to node's /transaction/build endpoint

      If no contracts have been added

      const builder = TransactionBuilder.create()
      .sender('klv1...')
      .transfer({ receiver: 'klv1...', amount: '1000000' })

      const request = builder.buildRequest()
      // Send request to node via HTTP:
      // POST /transaction/build
      // Body: request
    • Add multiple build options at once Convenience method to set multiple builder options in a single call. This is particularly useful when working with smart contracts or offline building.

      Note: The value option is not supported here - set callValue directly in smartContract()

      Parameters

      • options: BuildCallOptions

        Build options object

        Build call options All fields are optional - will use builder's state if not provided

        • OptionalchainId?: string

          Chain ID for offline transaction building

        • Optionaldata?: string[]

          Transaction data (for smart contract calls)

        • Optionalfees?: { bandwidthFee: number; kAppFee: number }

          Fees for offline transaction building

        • OptionalkdaFee?: { amount: AmountLike; kda: string }

          KDA fee for offline transaction building

        • Optionalnonce?: number

          Nonce for offline transaction building

        • OptionalpermissionId?: number

          Permission ID for the transaction

        • Optionalsender?: string

          Sender address

        • Optionalvalue?: Record<string, bigint>

          Value to send with transaction (e.g., { KLV: parseKLV('1') })

      Returns this

      This builder instance for chaining

      // Set multiple options at once
      const tx = TransactionBuilder.create()
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .callOptions({
      sender: 'klv1...',
      nonce: 123,
      chainId: '100',
      permissionId: 1
      })
      .buildProto({ fees: { kAppFee: 500000, bandwidthFee: 100000 } })
    • Add claim contract to claim rewards or allocations Used for claiming staking rewards, airdrops, or other claimable amounts

      Parameters

      • params: ClaimRequest

        Claim parameters

        • claimType: number
        • Optionalid?: string

      Returns this

      This builder instance for chaining

      // Claim staking rewards
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .claim({
      claimType: 0 // Staking rewards
      })
      .build()

      // Claim specific allocation
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .claim({
      claimType: 1,
      id: 'allocation-id-123'
      })
      .build()
    • Add create asset contract to create a new token or NFT collection Creates fungible tokens (FTs), non-fungible tokens (NFTs), or other asset types

      Parameters

      • params: CreateAssetRequest

        Asset creation parameters

        • OptionaladminAddress?: string
        • Optionalattributes?: AttributesInfo
        • OptionalinitialSupply?: AmountLike
        • Optionallogo?: string
        • maxSupply: AmountLike
        • name: string
        • ownerAddress: string
        • precision: number
        • Optionalproperties?: PropertiesInfo
        • Optionalroles?: RolesInfo[]
        • Optionalroyalties?: RoyaltiesInfo
        • Optionalstaking?: StakingInfo
        • ticker: string
        • type: number
        • Optionaluris?: Record<string, string>

      Returns this

      This builder instance for chaining

      // Create fungible token
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .createAsset({
      type: 0,
      name: 'My Token',
      ticker: 'MTK',
      ownerAddress: 'klv1...',
      precision: 6,
      maxSupply: '1000000000000',
      initialSupply: '100000000000'
      })
      .build()

      // Create NFT collection
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .createAsset({
      type: 1,
      name: 'My NFT Collection',
      ticker: 'MYNFT',
      ownerAddress: 'klv1...',
      precision: 0,
      maxSupply: 0,
      royalties: { address: 'klv1...', percentage: 5 }
      })
      .build()
    • Add create validator contract to register a new validator node Validators participate in consensus and earn rewards for securing the network

      Parameters

      • params: CreateValidatorRequest

        Validator creation parameters

        • blsPublicKey: string
        • OptionalcanDelegate?: boolean
        • commission: number
        • Optionallogo?: string
        • OptionalmaxDelegationAmount?: AmountLike
        • Optionalname?: string
        • ownerAddress: string
        • OptionalrewardAddress?: string
        • Optionaluris?: Record<string, string>

      Returns this

      This builder instance for chaining

      // Create validator node
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .createValidator({
      blsPublicKey: '0xabcd1234...',
      ownerAddress: 'klv1...',
      commission: 10, // 10% commission
      canDelegate: true,
      name: 'My Validator',
      logo: 'https://example.com/logo.png',
      uris: {
      website: 'https://validator.example.com',
      twitter: 'https://twitter.com/myvalidator'
      }
      })
      .build()
    • Set transaction data for smart contract calls Data is used primarily for smart contract interactions, where it contains:

      • Function name (first element)
      • Function arguments (remaining elements)

      Important:

      • Data is automatically base64 encoded when building with buildRequest()
      • For offline building with buildProto(), provide UTF-8 strings

      Parameters

      • data: string[]

        Array of strings containing function name and arguments

      Returns this

      This builder instance for chaining

      // Smart contract call with arguments
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .smartContract({ address: 'klv1contract...', scType: 1 })
      .data(['transfer', 'klv1receiver...', '1000000'])
      .build()

      // Multiple data fields
      const tx = TransactionBuilder.create()
      .sender('klv1...')
      .smartContract({ address: 'klv1contract...', scType: 1 })
      .data(['functionName', 'arg1', 'arg2', 'arg3'])
      .buildProto({ nonce: 1, chainId: '100', fees: { kAppFee: 500000, bandwidthFee: 100000 } })
    • Add delegate contract to assign a frozen bucket to a validator Delegation allows validators to use your staked KLV for consensus and earn rewards

      Parameters

      • params: DelegateRequest

        Delegate parameters

        • OptionalbucketId?: string
        • receiver: string

      Returns this

      This builder instance for chaining

      If validator address is invalid

      // Delegate specific bucket to validator
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .delegate({
      receiver: 'klv1validator123...',
      bucketId: 'bucket-hash-123'
      })
      .build()

      // Delegate all available buckets
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .delegate({
      receiver: 'klv1validator123...'
      })
      .build()
    • Add freeze (stake) contract to lock KLV or KDA assets Freezing creates a bucket that can be delegated to validators or used for governance

      Parameters

      Returns this

      This builder instance for chaining

      If amount is not positive

      // Freeze KLV for staking
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .freeze({
      amount: '5000000' // 5 KLV
      })
      .build()

      // Freeze custom KDA token
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .freeze({
      amount: '1000000',
      kda: 'MYTOKEN-ABCD'
      })
      .build()
    • Set KDA fee to pay transaction fees in a custom KDA asset instead of KLV By default, transactions pay fees in KLV (kAppFee + bandwidthFee). This method allows paying fees in a different asset.

      Important:

      • Cannot use 'KLV' as kdaFee (KLV is the default fee asset)
      • The asset must support being used as a fee payment option

      Parameters

      • fee: { amount: AmountLike; kda: string }

        KDA fee configuration

        • amount: AmountLike

          Fee amount in smallest units of the KDA asset

        • kda: string

          Asset ID to use for fee payment (cannot be 'KLV')

      Returns this

      This builder instance for chaining

      If kda is 'KLV' or amount is negative

      // Pay fees in custom token instead of KLV
      const tx = TransactionBuilder.create()
      .sender('klv1...')
      .kdaFee({ kda: 'MYTOKEN-ABCD', amount: '1000000' })
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .buildProto({
      nonce: 1,
      chainId: '100',
      })
    • Set nonce manually for offline transaction building The nonce is a sequential counter that prevents transaction replay attacks. Each account has its own nonce that increments with every transaction.

      When to use:

      • Offline transaction building (required with buildProto())
      • Manual nonce management for batch transactions
      • Testing or debugging specific scenarios

      Getting current nonce: Use provider.getAccount(address) to get the current nonce from the network

      Parameters

      • nonce: number

        Transaction nonce (must be non-negative)

      Returns this

      This builder instance for chaining

      If nonce is negative

      // Manual nonce for offline building
      const tx = TransactionBuilder.create()
      .sender('klv1...')
      .nonce(123)
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .buildProto({
      chainId: '100',
      fees: { kAppFee: 500000, bandwidthFee: 100000 }
      })

      // Get nonce from provider first
      const account = await provider.getAccount('klv1...')
      const tx = TransactionBuilder.create()
      .sender('klv1...')
      .nonce(account.nonce)
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .buildProto({ chainId: '100', fees: { kAppFee: 500000, bandwidthFee: 100000 } })
    • Set permission ID for multi-signature transactions Permission IDs enable complex account structures with multiple signers and permissions.

      Use cases:

      • Multi-signature wallets requiring multiple approvals
      • Corporate accounts with different permission levels
      • Smart contract interactions with specific permissions

      Parameters

      • id: number

        Permission ID number

      Returns this

      This builder instance for chaining

      // Transaction requiring specific permission
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .permissionId(2)
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .build()
    • Reset builder state to initial values Clears all contracts and builder configuration, allowing reuse of the builder instance

      What gets reset:

      • All added contracts
      • Sender address
      • Nonce
      • KDA fee
      • Permission ID
      • Transaction data

      What persists:

      • Provider (if set)
      • Chain ID (if set)

      Returns this

      This builder instance for chaining

      const builder = TransactionBuilder.create(provider)

      // Build first transaction
      const tx1 = await builder
      .sender('klv1...')
      .transfer({ receiver: 'klv1abc...', amount: '1000000' })
      .build()

      // Reset and build second transaction
      const tx2 = await builder
      .reset()
      .sender('klv1...')
      .freeze({ amount: '5000000' })
      .build()
    • Set sender address for the transaction

      Parameters

      • address: string

        Bech32 encoded Klever address (e.g., "klv1...")

      Returns this

      This builder instance for chaining

      If address format is invalid

      const tx = await TransactionBuilder.create(provider)
      .sender('klv1fpwjz234gy8aaae3gx0e8q9f52vymzzn3z5q0s5h60pvktzx0n0qwvtux5')
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .build()
    • Set chain ID (overrides provider's network if set) The chain ID identifies which Klever network to use (e.g., "100" for mainnet)

      Parameters

      • chainId: string

        Chain ID string (e.g., "100" for mainnet, "101" for testnet)

      Returns this

      This builder instance for chaining

      const tx = TransactionBuilder.create()
      .setChainId('100')
      .sender('klv1...')
      .transfer({ receiver: 'klv1...', amount: '1000000' })
      .buildProto({ nonce: 1, fees: { kAppFee: 500000, bandwidthFee: 100000 } })
    • Add smart contract call to interact with deployed contracts Enables calling functions on smart contracts deployed on the Klever blockchain

      Contract Call Types (scType):

      • 0: Deploy contract
      • 1: Invoke contract function
      • 2: Upgrade contract

      Important:

      • Use .data() to specify function name and arguments
      • callValue allows sending KLV or KDA tokens with the call
      • Contract address must be valid bech32 format

      Parameters

      Returns this

      This builder instance for chaining

      If contract address is invalid

      // Invoke contract function
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .smartContract({
      address: 'klv1contract...',
      scType: 1, // Invoke
      callValue: { KLV: '1000000' } // Send 1 KLV
      })
      .data(['transfer', 'klv1receiver...', '500000'])
      .build()

      // Call contract without sending value
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .smartContract({
      address: 'klv1contract...',
      scType: 1
      })
      .data(['getValue'])
      .build()
    • Add transfer contract to send KLV or KDA assets

      Parameters

      Returns this

      This builder instance for chaining

      If receiver address is invalid or amount is not positive

      // Transfer KLV
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .transfer({
      receiver: 'klv1abc123...',
      amount: '1000000' // 1 KLV
      })
      .build()

      // Transfer custom KDA token
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .transfer({
      receiver: 'klv1abc123...',
      amount: '5000000',
      kda: 'MYTOKEN-ABCD'
      })
      .build()

      // Transfer with royalties (for NFTs)
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .transfer({
      receiver: 'klv1abc123...',
      amount: '1',
      kda: 'NFT-COLLECTION/NONCE-1',
      kdaRoyalties: '100000',
      klvRoyalties: '50000'
      })
      .build()
    • Add undelegate contract to remove delegation from a validator Undelegation returns the bucket to your control but keeps it frozen

      Parameters

      Returns this

      This builder instance for chaining

      If bucketId is missing

      // Undelegate bucket from validator
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .undelegate({
      bucketId: 'bucket-hash-123'
      })
      .build()
    • Add unfreeze (unstake) contract to unlock frozen assets Unfreezing initiates the unlocking process - assets become available after the unlock period

      Parameters

      • params: UnfreezeRequest

        Unfreeze parameters

        • OptionalbucketId?: string
        • kda: string

      Returns this

      This builder instance for chaining

      If kda parameter is missing

      // Unfreeze KLV bucket
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .unfreeze({
      kda: 'KLV',
      bucketId: 'bucket-hash-123'
      })
      .build()

      // Unfreeze custom KDA token
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .unfreeze({
      kda: 'MYTOKEN-ABCD'
      })
      .build()
    • Add vote contract to participate in governance proposals Voting allows token holders to participate in network governance decisions

      Parameters

      Returns this

      This builder instance for chaining

      // Vote yes on proposal
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .vote({
      proposalId: 5,
      type: 1, // Yes
      amount: '1000000' // Optional voting weight
      })
      .build()

      // Vote no on proposal
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .vote({
      proposalId: 5,
      type: 2 // No
      })
      .build()
    • Add withdraw contract to retrieve available funds Used to withdraw staking rewards, unlocked frozen assets, or other withdrawable amounts

      Parameters

      • params: WithdrawRequest

        Withdraw parameters

        • Optionalamount?: AmountLike
        • OptionalcurrencyID?: string
        • Optionalkda?: string
        • withdrawType: number

      Returns this

      This builder instance for chaining

      // Withdraw staking rewards
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .withdraw({
      withdrawType: 0 // Staking rewards
      })
      .build()

      // Withdraw specific KDA amount
      const tx = await TransactionBuilder.create(provider)
      .sender('klv1...')
      .withdraw({
      withdrawType: 0,
      kda: 'MYTOKEN-ABCD',
      amount: '1000000'
      })
      .build()