Skip to content

ethers signer ​

You write ethers. BkcSigner returns an ordinary ethers v6 Signer; Contract, ContractFactory, tx.wait() and queryFilter behave as on any EVM chain.

Create a signer ​

ts
import { BkcSigner, GasPrice } from "@bkc-network-lib/sdk";

const endpoints = {
  evmRpcUrl: "https://rpc.bkc.network",           // reads
  cosmosRpcUrl: "https://comet-rpc.bkc.network",  // writes are broadcast here
  gasPrice: GasPrice.fromString("5000000bkc"),
};

const signer = await BkcSigner.fromMnemonic(process.env.MNEMONIC!, endpoints);
// or: await BkcSigner.fromSeed(seedBytes, endpoints)       // 32-byte ML-DSA-65 seed
// or: await BkcSigner.fromWallet(offlineDirectSigner, endpoints)

Always pass gasPrice

Without it the SDK falls back to bkcChain.gasPrice, 66,666,666,667 bkc per gas — about 13,000× the chain minimum. Transactions still succeed, but each one costs that much more (a native send ≈ 0.017 BKC instead of ≈ 0.0000013 BKC).

Deploy, write, read ​

ts
import { ContractFactory, Contract } from "@bkc-network-lib/sdk";

const factory = new ContractFactory(abi, bytecode, signer);
const contract = await factory.deploy();
await contract.waitForDeployment();

await (await contract.set(42)).wait();   // a write: MsgExecuteEvmCall, signed with ML-DSA-65
await contract.get();                    // a read: eth_call, no signing → 42n

Native transfers are writes too:

ts
const tx = await signer.sendTransaction({ to: "0x…", value: 10n ** 18n }); // 1 BKC
await tx.wait();

What works ​

Measured against the live chain:

Works unchangedCannot work (throws with an explanation)
getNetwork, getBlockNumber, getBalance, getBlocksigner.signMessage, signTypedData — secp256k1 only
ContractFactory.deploy(), contract.method(), estimateGaseth_sendRawTransaction
tx.wait(), getTransaction, receipt log decodingqueryFilter with no block range — the node caps a log query at 10,000 blocks
queryFilter(filter, fromBlock, toBlock)
signer.sendTransaction({ to, value })

ethers nests the explanation one level down, at error.error.message; error.shortMessage only says "could not coalesce error".

Replacements ​

Instead ofUseYou get
receipt.fee (always 0)getEvmTransactionCost(hash, restUrl)the fee actually charged, the Cosmos hash and gas
queryFilter with no rangegetLogsPaged(provider, filter, { maxWindows })logs walked backwards one 10,000-block window at a time
signer.signMessagesignMessage(wallet, text)an ADR-36 document signed with ML-DSA-65
ts
import { getEvmTransactionCost, getLogsPaged, signMessage, verifyMessage } from "@bkc-network-lib/sdk";

const cost = await getEvmTransactionCost(receipt.hash, "https://api.bkc.network");

// getLogsPaged takes an EIP-1193 provider and a plain { address, topics } filter
import { JsonRpcProvider, id } from "ethers";
const rpc = new JsonRpcProvider("https://rpc.bkc.network");
const eip1193 = { request: ({ method, params }) => rpc.send(method, params ?? []) };

const { logs, reachedBlock } = await getLogsPaged(
  eip1193,
  { address: tokenAddress, topics: [id("Transfer(address,address,uint256)")] },
  { maxWindows: 5 },          // 5 × 10,000 blocks, newest first
);
// reachedBlock: the oldest block searched — continue from there if you need more history

Concurrency ​

call_nonce is the account's Cosmos sequence, and the chain requires it to match exactly. Two writes sent at once from the same signer would collide; send them one after another (await tx.wait() between) or use separate accounts.

Lower-level pieces ​

For custom tooling: makeEvmCallMessage, withGasHeadroom, cosmosGasForEvmCall, findEthereumTxHash, toEvmAddress, tryToEvmAddress, and createBkcEip1193Provider() — a raw EIP-1193 provider for libraries that wire their own stack (viem, wallets).

BKC testnet is unaudited. Do not hold value on it.