LumiBit's EVM is primarily based on Scroll's ZK rollup solution, and its implementation is largely consistent with most public blockchains. However, there are several key differences.
Address Generation Method
The LumiBit network uses the same address generation method as Bitcoin's P2WPKH to enable interoperability between the Bitcoin and LumiBit networks. This modification allows our platform to become a true Bitcoin Layer2.
Addresses are generated in the same way as Bitcoin. You can use the bitcore-lib library or bitcoinjs for this purpose.
Using bitcore-lib for generation:
// Generate a random key pair with compression enabledconstpair=ECPair.makeRandom({ compressed:true })// Output the private key in hexadecimal formatconsole.log(`Private Key: 0x${pair.privateKey?.toString('hex')}`)// Generate and display a Testnet BTC addressconsole.log(`Testnet BTC Address: ${bitcore.PublicKey(pair.publicKey, { compressed: true }).toAddress("testnet", "witnesspubkeyhash").toString()}`)
// Generate and display a Live BTC addressconstb=bitcore.PublicKey(pair.publicKey, { compressed:true }).toAddress("livenet","witnesspubkeyhash").toString()
Using bitcoinjs for generation:
constbitcoin=require('bitcoinlib');const { ECPair } = bitcoin;// Generate a random key pair with compressionconstpair=ECPair.makeRandom({ compressed:true });constpublicKey=pair.publicKey;// Generate a P2WPKH address using the public keyconst { address } =bitcoin.payments.p2wpkh({ pubkey: publicKey });// Display the generated P2WPKH addressconsole.log('P2WPKH address:', address);
Transaction Signing Method
The LumiBit network adopts a native signing mode, allowing full control over BTC wallets. You can sign transactions directly using various BTC wallets without needing external solutions like AA wallets for verification. This significantly reduces transaction fees and verification speeds, while also providing the convenience of native signatures.
Details about the signature modifications:
// Sign a Lumibit-specific message asynchronouslyasync signLumiBitMessage(text: string): Promise<string> { const message =newbitcore.Message(text); const pKey =newbitcore.PrivateKey(this.ecPair.toWIF()); return message.sign(pKey);}// Asynchronously sign a transactionasync signTransaction(tx: ethers.TransactionRequest): Promise<string> { const t =Transaction.from(<TransactionLike<string>>tx); const rawTx = _serializeLumiBit(t); const signed = Buffer.from(await this.signLumiBitMessage(keccak256(rawTx)), "base64").toString('hex') return _serializeLumiBit(t, Signature.from({ r: `0x${signed.slice(2,66)}`, s: `0x${signed.slice(66,130)}`, v: BigInt(`0x${signed.slice(0,2)}`) -4n, }))}// Serialize the transaction with or without a signaturefunction _serializeLumiBit(tx: Transaction | LumiBitTransaction, sig?: Signature): string { const fields: any = [formatNumber(tx.chainId,"chainId"),formatNumber(tx.nonce,"nonce"),formatNumber(tx.gasPrice ||0,"gasPrice"),formatNumber(tx.gasLimit,"gasLimit"), (tx.to ||"0x"),formatNumber(tx.value,"value"),tx.data,formatAccessList(tx.accessList || []) ];if (sig) { fields.push(toBeArray(sig.v)); fields.push(toBeArray(sig.r)); fields.push(toBeArray(sig.s)); } return concat(["0x04",encodeRlp(fields)]);}
API Modifications
Due to the use of Bitcoin addresses, most RPC requests in the LumiBit network will utilize the P2WPKH address format. This should be considered when addressing differences with traditional EVM RPC data.
// Process a transaction request for JSON-RPC, ensuring proper format adherencegetRpcTransaction(tx: TransactionRequest): JsonRpcTransactionRequest {constresult:JsonRpcTransactionRequest= {};// Ensure numeric values are converted to "quantity" values for JSON-RPC compliance ["chainId", "gasLimit", "gasPrice", "type", "maxFeePerGas", "maxPriorityFeePerGas", "nonce", "value"].forEach((key) => {
if ((<any>tx)[key] == null) { return; } let dstKey = key; if (key === "gasLimit") { dstKey ="gas"; } (<any>result)[dstKey] = toQuantity(getBigInt((<any>tx)[key], `tx.${ key }`)); }); // Ensure addresses and data are in lowercase ["from", "to"].forEach((key) => {if ((<any>tx)[key] == null) { return; } (<any>result)[key] = evmAddressToBech32((<any>tx)[key], this.btcNetwork); }); ["data"].forEach((key) => {if ((<any>tx)[key] == null) { return; } (<any>result)[key] = hexlify((<any>tx)[key]); }); // Normalize the access list if (tx.accessList) { result["accessList"] =accessListify(tx.accessList); } return result;}
All parameters and returned addresses adhere to the P2WPKH format.