spb/os-vault Public
Self-custody, multi-chain crypto wallet for macOS. One recovery phrase, six chain families, zero API keys — nothing leaves your Mac.
Swift 96%
Shell 3.4%
Makefile 0.6%
1import Foundation23/// Blockhash as Base58 string.4public typealias BlockHash = String56/// Lecagy message7public struct Message: IMessage, Equatable {8 public var version: TransactionVersion { .legacy }910 // MARK: - Constants1112 private static let RECENT_BLOCK_HASH_LENGTH = 321314 // MARK: - Properties1516 public internal(set) var header: MessageHeader17 public internal(set) var accountKeys: [PublicKey]18 public internal(set) var recentBlockhash: BlockHash19 public internal(set) var instructions: [CompiledInstruction]20 public var staticAccountKeys: [PublicKey] { accountKeys }2122 init(header: MessageHeader, accountKeys: [PublicKey], recentBlockhash: BlockHash,23 instructions: [CompiledInstruction])24 {25 self.header = header26 self.accountKeys = accountKeys27 self.recentBlockhash = recentBlockhash28 self.instructions = instructions29 }3031 public func serialize() throws -> Data {32 // Header33 let header = encodeHeader()3435 // Account keys36 let accountKeys = encodeAccountKeys()3738 // RecentBlockHash39 let recentBlockhash = encodeRecentBlockhash()4041 // Compiled instruction42 let compiledInstruction = try encodeInstructions()4344 // Construct data45 // let bufferSize: Int =46 // Header.LENGTH // header47 // + keyCount.count // number of account keys48 // + Int(accountKeys.count) * PublicKey.LENGTH // account keys49 // + RECENT_BLOCK_HASH_LENGTH // recent block hash50 // + instructionsLength.count51 // + compiledInstructionsLength5253 var data = Data( /* capacity: bufferSize */ )5455 // Append data56 data.append(header)57 data.append(accountKeys)58 data.append(recentBlockhash)59 data.append(compiledInstruction)6061 return data62 }6364 static func from(data: Data) throws -> Message {65 var data = data6667 let numRequiredSignatures = data.removeFirst()68 let numReadonlySignedAccounts = data.removeFirst()69 let numReadonlyUnsignedAccounts = data.removeFirst()7071 let accountCount = data.decodeLength()72 var accountKeys: [PublicKey] = []73 for _ in 0 ..< accountCount {74 let account = data.prefix(PublicKey.numberOfBytes)75 data = data.dropFirst(PublicKey.numberOfBytes)76 try accountKeys.append(PublicKey(string: Base58.encode(account.bytes)))77 }7879 let recentBlockhash = data.prefix(PublicKey.numberOfBytes)80 data = data.dropFirst(PublicKey.numberOfBytes)8182 let instructionCount = data.decodeLength()83 var instructions: [CompiledInstruction] = []84 for _ in 0 ..< instructionCount {85 let programIdIndex = data.removeFirst()86 let accountCount = data.decodeLength()87 let accounts = data.prefix(accountCount)88 data = data.dropFirst(accountCount)89 let dataLength = data.decodeLength()90 let dataSlice = data.prefix(dataLength)91 data = data.dropFirst(dataLength)92 instructions.append(93 CompiledInstruction(94 programIdIndex: programIdIndex,95 keyIndicesCount: Data.encodeLength(accountCount).bytes,96 keyIndices: accounts.bytes,97 dataLength: Data.encodeLength(dataLength).bytes,98 data: dataSlice.bytes99 )100 )101 }102103 return Message(104 header: MessageHeader(numRequiredSignatures: Int(numRequiredSignatures),105 numReadonlySignedAccounts: Int(numReadonlySignedAccounts),106 numReadonlyUnsignedAccounts: Int(numReadonlyUnsignedAccounts)),107 accountKeys: accountKeys,108 recentBlockhash: Base58.encode(recentBlockhash.bytes),109 instructions: instructions110 )111 }112113 public func isAccountWritable(index: Int) -> Bool {114 (index < header.numRequiredSignatures - header.numReadonlySignedAccounts)115 ||116 (index >= header.numRequiredSignatures && index < accountKeys.count - header117 .numReadonlyUnsignedAccounts)118 }119120 public func isAccountSigner(index: Int) -> Bool {121 index < header.numRequiredSignatures122 }123124 private func encodeHeader() -> Data {125 Data(header.bytes)126 }127128 private func encodeAccountKeys() -> Data {129 // length130 let keyCount = encodeLength(accountKeys.count)131132 // construct data133 var data = Data(capacity: keyCount.count + accountKeys.count * PublicKey.numberOfBytes)134135 // append data136 data.append(keyCount)137 for key in accountKeys {138 data.append(key.data)139 }140 return data141 }142143 private func encodeRecentBlockhash() -> Data {144 Data(Base58.decode(recentBlockhash))145 }146147 private func encodeInstructions() throws -> Data {148 let instructionsLength = Data.encodeLength(instructions.count)149 return instructionsLength + instructions.reduce(Data()) { $0 + $1.serializedData }150 }151152 private func encodeLength(_ length: Int) -> Data {153 Data.encodeLength(length)154 }155}156