Skip to main content
This guide covers Hedera-specific concepts when using Dfns, including address formats, transaction types, and smart contract interactions.

Address formats

Hedera uses two address formats:
FormatExampleWhen to use
Native0.0.1234567Hedera-native operations, account references
EVM0x00000000000000000000000000000012d687Smart contract interactions, EVM compatibility

Converting between formats

Native to EVM:
function nativeToEvm(accountId: string): string {
  const [shard, realm, num] = accountId.split('.').map(Number)
  // Hedera EVM addresses encode shard.realm.num in the last 20 bytes
  const evmAddress =
    '0x' +
    shard.toString(16).padStart(8, '0') +
    realm.toString(16).padStart(8, '0') +
    num.toString(16).padStart(8, '0')
  return evmAddress
}

// Example: "0.0.1234567" -> "0x000000000000000000000000000012d687"
EVM to Native:
function evmToNative(evmAddress: string): string {
  const hex = evmAddress.slice(2) // remove 0x
  const shard = parseInt(hex.slice(0, 8), 16)
  const realm = parseInt(hex.slice(8, 16), 16)
  const num = parseInt(hex.slice(16, 24), 16)
  return `${shard}.${realm}.${num}`
}

// Example: "0x000000000000000000000000000012d687" -> "0.0.1234567"
Dfns wallets on Hedera return the native format (e.g., 0.0.1234567) in the address field. Convert to EVM format when needed for smart contract calls.

Transaction types

Native Hedera transactions

Use the Hiero/Hashgraph SDK to build native Hedera transactions:
import { Client, TransferTransaction, Hbar } from '@hashgraph/sdk'

const walletId = 'wa-xxxxx-xxxxx-xxxxxxxxxxxxxxxx'
const wallet = await dfnsClient.wallets.getWallet({ walletId })

// Create a Hedera client (for transaction building only)
const client = Client.forMainnet()

// Build the transaction
const transaction = await new TransferTransaction()
  .addHbarTransfer(recipient, Hbar.fromTinybars(1000000)) // 0.01 HBAR
  .addHbarTransfer(wallet.address!, Hbar.fromTinybars(-1000000))
  .freezeWith(client)

// Broadcast via Dfns
const res = await dfnsClient.wallets.broadcastTransaction({
  walletId,
  body: {
    kind: 'Transaction',
    transaction: `0x${Buffer.from(transaction.toBytes()).toString('hex')}`,
  },
})
Always call freezeWith(client) before serializing. This freezes the transaction with network parameters (node IDs, valid duration) required for signing.

Smart contract interactions

For smart contract calls, use ContractExecuteTransaction:
import {
  Client,
  ContractExecuteTransaction,
  ContractFunctionParameters,
  Hbar
} from '@hashgraph/sdk'

const contractId = '0.0.123456' // Your contract's native ID
const client = Client.forMainnet()

// Build contract call
const transaction = await new ContractExecuteTransaction()
  .setContractId(contractId)
  .setGas(100000)
  .setFunction(
    'transfer',
    new ContractFunctionParameters()
      .addAddress(recipientEvmAddress)
      .addUint256(amount)
  )
  .freezeWith(client)

// Broadcast via Dfns
const res = await dfnsClient.wallets.broadcastTransaction({
  walletId,
  body: {
    kind: 'Transaction',
    transaction: `0x${Buffer.from(transaction.toBytes()).toString('hex')}`,
  },
})

Common errors

”Transaction ID not found” or similar

This usually means the transaction wasn’t properly frozen before serialization. Solution: Ensure you call freezeWith(client) with a properly configured client:
// Correct
const client = Client.forMainnet() // or Client.forTestnet()
const tx = await new TransferTransaction()
  // ... add transfers
  .freezeWith(client) // Required!

// Then serialize
const bytes = tx.toBytes()

Invalid account format

If you’re getting address format errors, ensure you’re using the right format:
  • Native Hedera APIs expect 0.0.xxxxx
  • EVM/Solidity contracts expect 0x...
Use the conversion functions above to switch between formats.

Gas estimation failures

For contract calls, start with a higher gas limit and adjust:
.setGas(200000) // Start high, reduce based on actual usage
Check actual gas used in the transaction receipt to optimize.