// // Hex.swift // OS Vault // // Author: Simon-Pierre Boucher // Mail: contact@spboucher.ai // import Foundation import BigInt /// Minimal hex helpers for JSON-RPC quantities and calldata. public enum Hex { public static func quantity(_ value: BigUInt) -> String { "0x" + String(value, radix: 16) } public static func toBigUInt(_ hex: String) -> BigUInt? { let stripped = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex if stripped.isEmpty { return 0 } return BigUInt(stripped, radix: 16) } public static func data(_ hex: String) -> Data? { var stripped = hex.hasPrefix("0x") ? String(hex.dropFirst(2)) : hex if stripped.count % 2 != 0 { stripped = "0" + stripped } var out = Data(capacity: stripped.count / 2) var index = stripped.startIndex while index < stripped.endIndex { let next = stripped.index(index, offsetBy: 2) guard let byte = UInt8(stripped[index.. String { "0x" + data.map { String(format: "%02x", $0) }.joined() } /// Left-pads to a 32-byte ABI word. public static func abiWord(_ data: Data) -> Data { if data.count >= 32 { return data.suffix(32) } return Data(repeating: 0, count: 32 - data.count) + data } public static func abiWord(_ value: BigUInt) -> Data { abiWord(value.serialize()) } /// ERC-20 `transfer(address,uint256)` calldata. public static func erc20TransferData(to recipient: String, amount: BigUInt) -> Data? { guard let addressData = data(recipient), addressData.count == 20 else { return nil } var calldata = Data([0xa9, 0x05, 0x9c, 0xbb]) calldata.append(abiWord(addressData)) calldata.append(abiWord(amount)) return calldata } /// ERC-20 `balanceOf(address)` calldata. public static func erc20BalanceOfData(owner: String) -> Data? { guard let addressData = data(owner), addressData.count == 20 else { return nil } var calldata = Data([0x70, 0xa0, 0x82, 0x31]) calldata.append(abiWord(addressData)) return calldata } }