import { getContractAddress, Address, keccak256, encodePacked, Hex } from "viem" import { BitcoinAddressHelper } from "../bitcoin" /** * The address of the OrangeKit Safe Factory contract. The contract is deployed * under the same address on all supported networks. */ const ORANGEKIT_SAFE_FACTORY_ADDRESS = "0x7E80bd5A2E8Fca0B160254D43aC9f43AC2cC1052" /** * Bytecode Hash is calculated as a keccak256 hash of the concatenation of the * Safe Proxy bytecode and the Safe Singleton contract address used * by the OrangeKitSafeFactory contract's predictSafeAddress function. * keccak256( * encodePacked( * ["bytes", "uint256"], * [, ], * ) * ) * where: * - `SAFE_PROXY_BYTECODE` is bytecode of the `@safe-global/safe-contracts/contracts/proxies/SafeProxy.sol` * contract, compiled upon the OrangeKit contracts deployment. * - `SAFE_SINGLETON_ADDRESS` is the address of the Safe Singleton contract used by * the OrangeKitSafeFactory contract. */ const BYTECODE_HASH = "0x2ebd335f0ee339c4d1338b7275d9b30185802a35ba476be7906f8fb67cfe6e96" export const InvalidTruncatedBitcoinAddressLengthError = new Error( "Truncated Bitcoin address must be 20 bytes long", ) /** * Predicts the safe address for a given truncated Bitcoin address. * @param {Hex} truncatedBitcoinAddress - The 20 byte-long truncated Bitcoin address * @returns {Address} The predicted safe address */ export function predictAddressFromTruncatedBitcoinAddress( truncatedBitcoinAddress: Hex, ): Address { if (truncatedBitcoinAddress.length !== 42) throw InvalidTruncatedBitcoinAddressLengthError const salt = keccak256(encodePacked(["bytes20"], [truncatedBitcoinAddress])) const predictedAddress = getContractAddress({ opcode: "CREATE2", from: ORANGEKIT_SAFE_FACTORY_ADDRESS, salt, bytecodeHash: BYTECODE_HASH, }) return predictedAddress } /** * Predicts the safe address for a given Bitcoin address. * @param {string} bitcoinAddress - The bitcoin address of type P2WPKH, P2PKH or P2SH_P2WPKH * @param {string} publicKey - The public key of the bitcoin address to recover the truncated address * @returns {Address} The predicted safe address */ export default function predictOrangeKitAddress( bitcoinAddress: string, publicKey?: string, ): Address { const truncatedBitcoinAddress = BitcoinAddressHelper.recoverTruncatedBitcoinAddress( bitcoinAddress, publicKey, ) as Hex return predictAddressFromTruncatedBitcoinAddress(truncatedBitcoinAddress) }