Programmatically create a Hedera account with AccountCreateTransaction, set an ECDSA key with EVM Address from Public Key, and fund the new account with HBAR.
A Hedera account is required to interact with any network service, since every transaction and query fee is paid from an account. You can create a previewnet or testnet account on the Hedera Developer Portal, or use a third-party wallet to generate a free mainnet account.This page covers programmatic account creation with AccountCreateTransaction(). The transaction must be signed and paid for by an existing account. To obtain the new account ID, request the receipt of the transaction.For a complete list of account properties, see the accounts overview.
Recommended default for EVM compatibility: Create accounts with an ECDSA key and set the EVM Address from Public Key at creation. This enables native compatibility with EVM wallets, JSON-RPC tooling, and smart-contract interactions. Use setECDSAKeyWithAlias() (or setKeyWithAlias() with an ECDSA key) to do this in a single call.
Exactly one of setKey, setKeyWithAlias, setECDSAKeyWithAlias, or setKeyWithoutAlias is required. See Setting the key and alias for which to use.
Method
Type
Key type accepted
Requirement
setKey(<key>)
Key
PublicKey or PrivateKey
Sets the account key with no EVM Address from Public Key. Accepts ED25519 or ECDSA.
setKeyWithAlias(<key>)
Key (ECDSA)
PublicKey or PrivateKey
Sets the key and the EVM Address from Public Key. Java’s one-argument recommended form; in other SDKs this is the two-argument overload setKeyWithAlias(key, aliasKey).
setECDSAKeyWithAlias(<key>)
Key (ECDSA)
PrivateKey only in Rust, C++, Swift; PublicKey or PrivateKey elsewhere
Recommended for EVM use cases. Sets the key and the EVM Address from Public Key in one call. Not available in Java, which uses the one-argument setKeyWithAlias instead.
setKeyWithoutAlias(<key>)
Key
PublicKey or PrivateKey
Sets the key with no EVM Address from Public Key (the account falls back to its EVM Address from Account ID). Use if you plan to rotate keys later.
setAlias(<alias>)
EvmAddress
—
Explicitly sets the alias bytes. Pair with publicKey.toEvmAddress() to set the EVM Address from Public Key.
Setting an ECDSA-derived EVM address at creation makes the account natively addressable from EVM wallets, JSON-RPC, and Solidity (msg.sender). The address is the rightmost 20 bytes of the Keccak-256 hash of the ECDSA public key. To enable this behavior, use the one-argument key-with-alias method for your SDK (setECDSAKeyWithAlias() in most SDKs, setKeyWithAlias() in Java). See Setting the key and alias for the full breakdown.Immutability: The EVM address is bound to the original ECDSA public key and does not change if you later rotate keys via CryptoUpdateTransaction. Integrations keyed to that EVM address (smart-contract permissions, address-based access lists) will continue to reference the original address.If key rotation is required: Use setKeyWithoutAlias() instead. The account will fall back to its EVM Address from Account ID (the long-zero form).Recovery model: If keys are compromised or must be replaced, create a new account with a new ECDSA key, then migrate assets and state. Do not rely on key rotation to preserve the same EVM identity.
This transaction supports high-volume entity creation (HIP-1313). Setting setHighVolume(true) routes the transaction through dedicated high-volume throttle capacity with variable-rate pricing. Always pair this with setMaxTransactionFee() to cap your costs.
The maxAutoAssociations property determines how many automatic token associations an account allows.
Value
Behavior
0
Automatic token associations and token airdrops are not allowed. Tokens must be manually associated. This also applies when the value is less than or equal to usedAutoAssociations.
-1
Unlimited automatic token associations. This is the default for accounts created via auto account creation and for hollow accounts that have been completed. The sender still pays the association fee and initial rent for each new token.
> 0
Automatic token associations are limited to the specified number.
AccountCreateTransaction provides three ways to set the account key and the EVM address. Which one you choose depends on whether you want EVM compatibility and whether you plan to rotate the account key later. The method names below use the JavaScript SDK; see the per-SDK quick reference for the equivalent in each language.
Pattern
Method
What it does
Use when
Key with matching EVM address (recommended)
setECDSAKeyWithAlias(key)
Sets the account key and derives the EVM Address from Public Key from the same ECDSA key. Accepts a private or public ECDSA key.
You want a standard EVM-compatible account. The signing key and the EVM address are backed by the same ECDSA key, so msg.sender, EVM wallets, and JSON-RPC all resolve correctly.
Key with a separate alias key
setKeyWithAlias(key, aliasKey)
Sets the account (signing) key to one key and derives the EVM address from a different ECDSA key. Both keys must sign the transaction.
The account is controlled by one key (for example an Ed25519 key, or a different ECDSA key) but you want the EVM address derived from a separate ECDSA key.
Key without an alias
setKeyWithoutAlias(key)
Sets only the account key, with no EVM Address from Public Key. The account falls back to its EVM Address from Account ID (long-zero form).
You plan to rotate or update the account key later. The EVM Address from Public Key is immutable once set, so omit it if the key is not final.
To assign a specific, pre-computed EVM address instead of deriving one from the key, combine setKeyWithoutAlias(key) with setAlias(evmAddress). This is the HIP-583 pattern, useful when you already hold the target EVM address (for example, when migrating an existing EVM address).
Whether the one-shot method accepts a public key or requires a private key depends on the SDK (see the per-SDK quick reference):
JavaScript, Java, Python, and Go accept either a public or a private ECDSA key. The EVM address is derived from the public component, so the choice is only about what key material you hand the builder. Pass a public key when the private key is held externally (a hardware wallet, HSM, or another party); the create transaction must still be signed by the corresponding private key.
Rust, C++, and Swift accept a private key only. If you hold just a public key (HSM or external-signer flows), use the manual path instead: set the key without an alias and set the alias explicitly from the derived address, for example setKeyWithoutAlias(publicKey) with setAlias(publicKey.toEvmAddress()). See the HSM-signing tutorials.
In the JavaScript SDK, the four derive variants are:
Variant
Call
Derived EVM address from a private account key
setECDSAKeyWithAlias(privateKey)
Derived EVM address from a public account key
setECDSAKeyWithAlias(publicKey)
Derived EVM address from a private alias key
setKeyWithAlias(accountKey, privateAliasKey)
Derived EVM address from a public alias key
setKeyWithAlias(accountKey, publicAliasKey)
(In Java, the one-argument forms use setKeyWithAlias(key) rather than setECDSAKeyWithAlias.) When you use a separate alias key, both the account key and the alias key must sign the transaction.
Java naming:setKeyWithAlias(key) with one argument is Java’s equivalent of setECDSAKeyWithAlias(key) in other SDKs. The two-argument overload setKeyWithAlias(key, ecdsaKey) is used when the account key and the alias-derivation key differ.Long-zero address parallel: Rust and C++ do not expose toEvmAddress() on AccountId. They expose to_solidity_address() / toSolidityAddress() instead, which returns the same long-zero form (the EVM Address from Account ID). Functionally equivalent, just a naming difference.Swift throws: Swift’s AccountId.toEvmAddress() is declared throws, so call sites need try (e.g., try accountId.toEvmAddress()).
// Create new ECDSA keyconst ecdsaPublicKey = PrivateKey.generateECDSA().publicKey;// Create the transactionconst transaction = new AccountCreateTransaction() // Sets the EVM Address from Public Key (recommended for EVM compatibility) .setECDSAKeyWithAlias(ecdsaPublicKey) // Use .setKeyWithoutAlias(ecdsaPublicKey) if you plan to rotate keys soon after creation .setInitialBalance(new Hbar(1));// Sign the transaction with the client operator private key and submit to a Hedera networkconst txResponse = await transaction.execute(client);//Request the receipt of the transactionconst receipt = await txResponse.getReceipt(client);//Get the account IDconst newAccountId = receipt.accountId;console.log("The new account ID is " + newAccountId);// v2.84.0
If you call setAlias(...) directly instead of a one-shot key-with-alias method, derive the EVM address from the ECDSA public key with the SDK helper:
// publicKey.toEvmAddress() returns the EVM Address from Public Key (hex, no 0x prefix)const evmAddress = ecdsaPublicKey.toEvmAddress();const transaction = new AccountCreateTransaction() .setKeyWithoutAlias(ecdsaPublicKey) .setAlias(evmAddress) .setInitialBalance(new Hbar(1));
In most cases, prefer setECDSAKeyWithAlias(publicKey), which sets the key and derives the alias in one call. Use the toEvmAddress helper when you need the value separately (for logging, validation, or pairing with setAlias).
Note on overloading:toEvmAddress is also exposed on AccountId in some SDKs. AccountId.toEvmAddress() returns the EVM Address from Account ID (the long-zero form), not the EVM Address from Public Key. For EVM compatibility, you want the PublicKey variant. AccountId.toEvmAddress() is not implemented in Rust and is named toSolidityAddress() in C++.
const info = await new AccountInfoQuery() .setAccountId(newAccountId) .execute(client);console.log(`EVM address: 0x${info.contractAccountId}`);
If the account was created with setKeyWithoutAlias(...), contractAccountId returns the EVM Address from Account ID (long-zero form, starting with 24 zero hex characters). If it was created with setECDSAKeyWithAlias(...) or equivalent, it returns the EVM Address from Public Key.
# Look up by account IDcurl https://mainnet.mirrornode.hedera.com/api/v1/accounts/0.0.1234# Look up by EVM addresscurl https://mainnet.mirrornode.hedera.com/api/v1/accounts/0xab...cd
The evm_address field in the response is the canonical EVM address for the account.
Creating an ECDSA account with setKey() only. The account is valid and can hold HBAR, but it has no EVM Address from Public Key. It falls back to its EVM Address from Account ID (long-zero form), which means:
It is not natively addressable from MetaMask or other EVM wallets keyed to the public-key EVM address.
Solidity contracts that compare msg.sender against the expected EVM address will not match.
JSON-RPC tooling expecting an ECDSA-derived address sees an unexpected long-zero address.
Use setECDSAKeyWithAlias(publicKey) (or setKeyWithAlias(publicKey) with an ECDSA key) instead.
Using deprecated PrivateKey.generate(). This helper silently returns an ED25519 key, which cannot be used to derive an EVM address. Use PrivateKey.generateECDSA() for new accounts.