SPB Git

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%
5.2 KB · 148 lines swift
Raw Blame History
1import Foundation23public struct CompiledKeyMeta {4    public var isSigner: Bool5    public var isWritable: Bool6    public var isInvoked: Bool78    public init(isSigner: Bool, isWritable: Bool, isInvoked: Bool) {9        self.isSigner = isSigner10        self.isWritable = isWritable11        self.isInvoked = isInvoked12    }13}1415public typealias KeyMetaMap = [String: CompiledKeyMeta]1617public struct CompiledKeys {18    public var payer: PublicKey19    public var keyMetaMap: KeyMetaMap2021    public init(payer: PublicKey, keyMetaMap: [String: CompiledKeyMeta]) {22        self.payer = payer23        self.keyMetaMap = keyMetaMap24    }2526    public mutating func extractTableLookup(lookupTable: AddressLookupTableAccount) throws -> (MessageAddressTableLookup, AccountKeysFromLookups)? {27        let (writableIndexes, drainedWritableKeys) = try drainKeysFoundInLookupTable(28            lookupTableEntries: lookupTable.state.addresses29        ) { keyMeta in30            !keyMeta.isSigner && !keyMeta.isInvoked && keyMeta.isWritable31        }3233        let (readonlyIndexes, drainedReadonlyKeys) = try drainKeysFoundInLookupTable(34            lookupTableEntries: lookupTable.state.addresses35        ) { keyMeta in36            !keyMeta.isSigner && !keyMeta.isInvoked && !keyMeta.isWritable37        }3839        if writableIndexes.count == 0, readonlyIndexes.count == 0 {40            return nil41        }4243        return (44            .init(45                accountKey: lookupTable.key,46                writableIndexes: writableIndexes,47                readonlyIndexes: readonlyIndexes48            ),49            .init(50                readonly: drainedReadonlyKeys,51                writable: drainedWritableKeys52            )53        )54    }5556    internal mutating func drainKeysFoundInLookupTable(57        lookupTableEntries: [PublicKey],58        keyMetaFilter: (CompiledKeyMeta) -> Bool59    ) throws -> ([UInt8], [PublicKey]) {60        var lookupTableIndexes: [UInt8] = []61        var drainedKeys: [PublicKey] = []6263        for (address, keyMeta) in keyMetaMap {64            if keyMetaFilter(keyMeta) {65                let key = try PublicKey(string: address)66                let lookupTableIndex = lookupTableEntries.firstIndex(of: key)67                if let lookupTableIndex = lookupTableIndex {68                    lookupTableIndexes.append(UInt8(lookupTableIndex))69                    drainedKeys.append(key)70                    keyMetaMap.removeValue(forKey: address)71                }72            }73        }7475        return (lookupTableIndexes, drainedKeys)76    }7778    func getMessageComponents() -> (MessageHeader, [PublicKey]) {79        let writableSigners = keyMetaMap.filter { _, meta in80            meta.isSigner && meta.isWritable81        }8283        let readonlySigners = keyMetaMap.filter { _, meta in84            meta.isSigner && !meta.isWritable85        }8687        let writableNonSigners = keyMetaMap.filter { _, meta in88            !meta.isSigner && meta.isWritable89        }9091        let readonlyNonSigners = keyMetaMap.filter { _, meta in92            !meta.isSigner && !meta.isWritable93        }9495        let header: MessageHeader = .init(96            numRequiredSignatures: writableSigners.count + readonlySigners.count,97            numReadonlySignedAccounts: readonlySigners.count,98            numReadonlyUnsignedAccounts: readonlyNonSigners.count99        )100101        var staticAccountKeys: [PublicKey] = []102        staticAccountKeys.append(contentsOf: writableSigners.map(\.key).map { try! PublicKey(string: $0) })103        staticAccountKeys.append(contentsOf: readonlySigners.map(\.key).map { try! PublicKey(string: $0) })104        staticAccountKeys.append(contentsOf: writableNonSigners.map(\.key).map { try! PublicKey(string: $0) })105        staticAccountKeys.append(contentsOf: readonlyNonSigners.map(\.key).map { try! PublicKey(string: $0) })106107        return (header, staticAccountKeys)108    }109110    static func compile(instructions: [TransactionInstruction], payer: PublicKey) -> Self {111        var keyMetaMap: KeyMetaMap = .init()112        var getOrInsertDefault: (PublicKey, (inout CompiledKeyMeta) -> Void) -> CompiledKeyMeta = { pubKey, callback in113            let address = pubKey.base58EncodedString114            if var keyMeta = keyMetaMap[address] {115                callback(&keyMeta)116                keyMetaMap[address] = keyMeta117                return keyMeta118            } else {119                var keyMeta = CompiledKeyMeta(120                    isSigner: false,121                    isWritable: false,122                    isInvoked: false123                )124                callback(&keyMeta)125                keyMetaMap[address] = keyMeta126                return keyMeta127            }128        }129130        _ = getOrInsertDefault(payer) { meta in131            meta.isSigner = true132            meta.isWritable = true133        }134135        for ix in instructions {136            _ = getOrInsertDefault(ix.programId) { meta in meta.isInvoked = true }137            for accountMeta in ix.keys {138                _ = getOrInsertDefault(accountMeta.publicKey) { meta in139                    meta.isSigner = meta.isSigner || accountMeta.isSigner140                    meta.isWritable = meta.isWritable || accountMeta.isWritable141                }142            }143        }144145        return .init(payer: payer, keyMetaMap: keyMetaMap)146    }147}148