Deploy a contract
Frost runs the EVM at the Osaka feature level. Solidity contracts deploy and run unchanged — same bytecode, same opcodes, same gas semantics, same precompiles at the same addresses. If it works on Ethereum, it works here.
With Foundry
Section titled “With Foundry”forge create src/MyContract.sol:MyContract \ --rpc-url https://rpc.frostfi.net \ --private-key $PRIVATE_KEY \ --priority-gas-price 0[rpc_endpoints]frost = "https://rpc.frostfi.net"With Hardhat
Section titled “With Hardhat”module.exports = { networks: { frost: { url: 'https://rpc.frostfi.net', chainId: 8141, accounts: [process.env.PRIVATE_KEY], }, },};Things to account for
Section titled “Things to account for”Set the priority fee to zero. Frost is fee-blind
and a tip buys nothing. eth_maxPriorityFeePerGas returns 0, so tooling that
queries it does the right thing automatically; tooling with a hardcoded tip
wastes gas.
Block gas limit is 60,000,000. Larger than Ethereum mainnet’s, so contracts that are tight on deployment gas have more room.
msg.sender from a post-quantum account is just an address. When a frame
transaction’s SENDER frame calls your contract, msg.sender is the account’s
address. Your contract cannot tell the difference between that and an EOA, and
does not need to.
Do not gate on tx.origin == msg.sender. That idiom is used to reject
contract callers, and it rejects every post-quantum account on this chain. It
was already discouraged; here it is actively wrong.
CREATE2 factory is a predeploy. The deterministic factory lives at
0x4e59b44847b379578588920cA78FbF26c0B4956C from genesis, so counterfactual
deployment patterns work without deploying a factory first. Multicall3 is also
predeployed. See Predeploys.
Calling the post-quantum precompiles
Section titled “Calling the post-quantum precompiles”Any contract can verify an ML-DSA signature. The precompiles have no ABI: pass
the raw byte string at fixed offsets and use staticcall.
/// @notice Verify an ML-DSA-65 (FIPS 204) signature./// @param message 32-byte message/// @param signature 3,309-byte FIPS 204 signature/// @param publicKey 1,952-byte FIPS 204 public keyfunction verifyMlDsa65( bytes32 message, bytes calldata signature, bytes calldata publicKey) internal view returns (bool) { bytes memory input = abi.encodePacked(message, signature, publicKey);
(bool ok, bytes memory out) = address(0x14).staticcall(input);
// 0x14 always returns exactly one 32-byte word: 1 valid, 0 invalid. // It never returns empty, so a length check is belt-and-braces here — // unlike ecrecover, where it is mandatory. return ok && out.length == 32 && abi.decode(out, (uint256)) == 1;}Gas: 16,000 flat for 0x14 (ML-DSA-65), 10,500 for 0x15 (ML-DSA-44),
regardless of input. Full specifications:
VERIFY_MLDSA65 and
VERIFY_MLDSA44.
Verifying source
Section titled “Verifying source”Contract verification is available through the block explorer at explorer.frostfi.net.
- Precompiled contracts — the full reference.
- Predeploys — fixed addresses you can rely on.
- Ecosystem — what is already deployed.