Skip to content

Commit e505803

Browse files
Add decompile TransactionMessage (MessageV0 only)
1 parent bc13101 commit e505803

File tree

4 files changed

+101
-7
lines changed

4 files changed

+101
-7
lines changed

Sources/SolanaSwift/Models/Message/IMessage.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ public protocol IMessage {
44
var version: TransactionVersion { get }
55
var header: MessageHeader { get }
66
var recentBlockhash: String { get }
7+
var staticAccountKeys: [PublicKey] { get }
78

89
func serialize() throws -> Data
9-
10-
var staticAccountKeys: [PublicKey] { get }
10+
func getAccountKeys(addressLookupTableAccounts: [AddressLookupTableAccount]) throws -> MessageAccountKeys
1111
}

Sources/SolanaSwift/Models/Message/LegacyMessage.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ public struct Message: IMessage, Equatable {
6060

6161
return data
6262
}
63+
64+
public func getAccountKeys(addressLookupTableAccounts: [AddressLookupTableAccount]) throws -> MessageAccountKeys {
65+
.init(staticAccountKeys: staticAccountKeys)
66+
}
6367

6468
static func from(data: Data) throws -> Message {
6569
var data = data

Sources/SolanaSwift/Models/Message/VersionedMessage.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,28 @@ public enum VersionedMessage: Equatable {
2424
case let .v0(message): return message
2525
}
2626
}
27+
28+
public var header: MessageHeader {
29+
value.header
30+
}
31+
32+
public var compiledInstructions: [MessageCompiledInstruction] {
33+
switch self {
34+
case .legacy:
35+
// need update
36+
return []
37+
case .v0(let value):
38+
return value.compiledInstructions
39+
}
40+
}
41+
42+
public var staticAccountKeys: [PublicKey] {
43+
value.staticAccountKeys
44+
}
45+
46+
public var recentBlockhash: BlockHash {
47+
value.recentBlockhash
48+
}
2749

2850
public mutating func setRecentBlockHash(_ blockHash: BlockHash) {
2951
switch self {
@@ -35,4 +57,8 @@ public enum VersionedMessage: Equatable {
3557
self = .v0(messageV0)
3658
}
3759
}
60+
61+
public func getAccountKeys(addressLookupTableAccounts: [AddressLookupTableAccount]) throws -> MessageAccountKeys {
62+
try value.getAccountKeys(addressLookupTableAccounts: addressLookupTableAccounts)
63+
}
3864
}

Sources/SolanaSwift/Models/Transaction/TransactionMessage.swift

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,74 @@
11
import Foundation
22

33
public struct TransactionMessage {
4-
var instructions: [TransactionInstruction]
5-
var recentBlockhash: String
6-
var payerKey: PublicKey
4+
public var instructions: [TransactionInstruction]
5+
public var recentBlockhash: String
6+
public var payerKey: PublicKey
77

88
public init(instructions: [TransactionInstruction], recentBlockhash: String, payerKey: PublicKey) {
99
self.instructions = instructions
1010
self.recentBlockhash = recentBlockhash
1111
self.payerKey = payerKey
1212
}
1313

14-
// TODO: implement
15-
// static func decompile() {}
14+
public static func decompile(message: VersionedMessage, addressLookupTableAccounts: [AddressLookupTableAccount]) throws -> Self {
15+
let header = message.header
16+
let compiledInstructions = message.compiledInstructions
17+
let recentBlockhash = message.recentBlockhash
18+
19+
let numRequiredSignatures = header.numRequiredSignatures
20+
let numReadonlySignedAccounts = header.numReadonlySignedAccounts
21+
let numReadonlyUnsignedAccounts = header.numReadonlyUnsignedAccounts
22+
23+
let numWritableSignedAccounts = numRequiredSignatures - numReadonlySignedAccounts
24+
guard numWritableSignedAccounts > 0 else {
25+
throw TransactionMessageError.invalidHeader
26+
}
27+
28+
let numWritableUnsignedAccounts = message.staticAccountKeys.count - numRequiredSignatures - numReadonlyUnsignedAccounts
29+
guard numWritableUnsignedAccounts >= 0 else {
30+
throw TransactionMessageError.invalidHeader
31+
}
32+
33+
guard let accountKeys = try? message.getAccountKeys(addressLookupTableAccounts: addressLookupTableAccounts) else {
34+
throw TransactionMessageError.noAccountKeys
35+
}
36+
37+
guard let payerKey = accountKeys[0] else {
38+
throw TransactionMessageError.noPayerKey
39+
}
40+
41+
var instructions: [TransactionInstruction] = []
42+
for compiledIx in compiledInstructions {
43+
var keys: [AccountMeta] = []
44+
for keyIndex in compiledIx.accountKeyIndexes {
45+
guard let pubkey = accountKeys[Int(keyIndex)] else {
46+
throw TransactionMessageError.keyNotFound
47+
}
48+
49+
let isSigner = keyIndex < numRequiredSignatures
50+
let isWritable: Bool
51+
if isSigner {
52+
isWritable = keyIndex < numWritableSignedAccounts
53+
} else if keyIndex < accountKeys.staticAccountKeys.count {
54+
isWritable = Int(keyIndex) - numRequiredSignatures < numWritableUnsignedAccounts
55+
} else {
56+
let writableCount = accountKeys.accountKeysFromLookups!.writable.count
57+
isWritable = Int(keyIndex) - accountKeys.staticAccountKeys.count < writableCount
58+
}
59+
60+
keys.append(AccountMeta(publicKey: pubkey, isSigner: isSigner, isWritable: isWritable))
61+
}
62+
63+
guard let programId = accountKeys[Int(compiledIx.programIdIndex)] else {
64+
throw TransactionMessageError.programIdNotFound
65+
}
66+
67+
instructions.append(TransactionInstruction(keys: keys, programId: programId, data: compiledIx.data))
68+
}
69+
70+
return TransactionMessage(instructions: instructions, recentBlockhash: recentBlockhash, payerKey: payerKey)
71+
}
1672

1773
public func compileToLegacyMessage() throws -> Message {
1874
try Transaction(
@@ -34,3 +90,11 @@ public struct TransactionMessage {
3490
)
3591
}
3692
}
93+
94+
public enum TransactionMessageError: Error, Equatable {
95+
case invalidHeader
96+
case noAccountKeys
97+
case noPayerKey
98+
case keyNotFound
99+
case programIdNotFound
100+
}

0 commit comments

Comments
 (0)