diff --git a/Source/TonSwift/Cells/Cell.swift b/Source/TonSwift/Cells/Cell.swift index d4ab0c4..f11e6d1 100644 --- a/Source/TonSwift/Cells/Cell.swift +++ b/Source/TonSwift/Cells/Cell.swift @@ -87,7 +87,7 @@ public struct Cell: Hashable { - parameter src: source buffer - returns array of cells */ - static func fromBoc(src: Data) throws -> [Cell] { + public static func fromBoc(src: Data) throws -> [Cell] { return try deserializeBoc(src: src) } @@ -95,7 +95,7 @@ public struct Cell: Hashable { Helper class that deserializes a single cell from BOC in base64 - parameter src: source string */ - static func fromBase64(src: String) throws -> Cell { + public static func fromBase64(src: String) throws -> Cell { guard let data = Data(base64Encoded: src) else { throw NSError() } diff --git a/Source/TonSwift/Jettons/JettonTransferData.swift b/Source/TonSwift/Jettons/JettonTransferData.swift new file mode 100644 index 0000000..63f45e5 --- /dev/null +++ b/Source/TonSwift/Jettons/JettonTransferData.swift @@ -0,0 +1,59 @@ +// +// JettonTransferData.swift +// +// +// Created by Grigory on 11.7.23.. +// + +import Foundation +import BigInt + +struct JettonTransferData: CellCodable { + let queryId: UInt64 + let amount: BigUInt + let toAddress: Address + let responseAddress: Address + let forwardAmount: BigUInt + let comment: String? + + func storeTo(builder: Builder) throws { + try builder.store(uint: 0xf8a7ea5, bits: 32) + try builder.store(uint: queryId, bits: 64) + try builder.store(coins: Coins(amount.magnitude)) + try builder.store(AnyAddress(toAddress)) + try builder.store(AnyAddress(responseAddress)) + try builder.store(bit: false) + try builder.store(coins: Coins(forwardAmount.magnitude)) + var commentCell: Cell? + if let comment = comment { + commentCell = try Builder().store(int: 0, bits: 32).writeSnakeData(Data(comment.utf8)).endCell() + } + try builder.store(bit: commentCell != nil) + try builder.storeMaybe(ref: commentCell) + } + + static func loadFrom(slice: Slice) throws -> JettonTransferData { + _ = try slice.loadUint(bits: 32) + let queryId = try slice.loadUint(bits: 64) + let amount = try slice.loadCoins().amount + let toAddress: Address = try slice.loadType() + let responseAddress: Address = try slice.loadType() + try slice.skip(1) + let forwardAmount = try slice.loadCoins().amount + + let hasComment = try slice.loadBoolean() + var comment: String? + if hasComment, let commentCell = try slice.loadMaybeRef() { + let slice = try commentCell.toSlice() + try slice.skip(32) + comment = try slice.loadSnakeString() + } + + return JettonTransferData(queryId: queryId, + amount: amount, + toAddress: toAddress, + responseAddress: responseAddress, + forwardAmount: forwardAmount, + comment: comment) + } +} diff --git a/Source/TonSwift/Jettons/JettonTransferMessage.swift b/Source/TonSwift/Jettons/JettonTransferMessage.swift new file mode 100644 index 0000000..ce02092 --- /dev/null +++ b/Source/TonSwift/Jettons/JettonTransferMessage.swift @@ -0,0 +1,34 @@ +// +// JettonTransferMessage.swift +// +// +// Created by Grigory on 11.7.23.. +// + +import Foundation +import BigInt + +public struct JettonTransferMessage { + public static func internalMessage(jettonAddress: Address, + amount: BigInt, + to: Address, + from: Address, + comment: String? = nil) throws -> MessageRelaxed { + let forwardAmount = BigUInt(stringLiteral: "1") + let jettonTransferAmount = BigUInt(stringLiteral: "640000000") + let queryId = UInt64(Date().timeIntervalSince1970) + + let jettonTransferData = JettonTransferData(queryId: queryId, + amount: amount.magnitude, + toAddress: to, + responseAddress: from, + forwardAmount: forwardAmount, + comment: comment) + + return MessageRelaxed.internal( + to: jettonAddress, + value: jettonTransferAmount, + body: try Builder().store(jettonTransferData).endCell() + ) + } +} diff --git a/Source/TonSwift/Keys/KeyPair.swift b/Source/TonSwift/Keys/KeyPair.swift index 9dfb115..efebd4a 100644 --- a/Source/TonSwift/Keys/KeyPair.swift +++ b/Source/TonSwift/Keys/KeyPair.swift @@ -7,7 +7,13 @@ import Foundation -public struct KeyPair { +public struct KeyPair: Codable { public let publicKey: PublicKey public let privateKey: PrivateKey + + public init(publicKey: PublicKey, + privateKey: PrivateKey) { + self.publicKey = publicKey + self.privateKey = privateKey + } } diff --git a/Source/TonSwift/Keys/PrivateKey.swift b/Source/TonSwift/Keys/PrivateKey.swift index 90851ac..404559b 100644 --- a/Source/TonSwift/Keys/PrivateKey.swift +++ b/Source/TonSwift/Keys/PrivateKey.swift @@ -7,7 +7,7 @@ import Foundation -public struct PrivateKey: Key, Equatable { +public struct PrivateKey: Key, Equatable, Codable { public let data: Data public init(data: Data) { diff --git a/Source/TonSwift/Keys/PublicKey.swift b/Source/TonSwift/Keys/PublicKey.swift index 9cc9918..af0f1bd 100644 --- a/Source/TonSwift/Keys/PublicKey.swift +++ b/Source/TonSwift/Keys/PublicKey.swift @@ -7,7 +7,7 @@ import Foundation -public struct PublicKey: Key { +public struct PublicKey: Key, Codable { public let data: Data public init(data: Data) { diff --git a/Source/TonSwift/NFT/NFTTransferData.swift b/Source/TonSwift/NFT/NFTTransferData.swift new file mode 100644 index 0000000..493caf9 --- /dev/null +++ b/Source/TonSwift/NFT/NFTTransferData.swift @@ -0,0 +1,48 @@ +// +// NFTTransferData.swift +// +// +// Created by Grigory on 25.8.23.. +// + +import Foundation +import BigInt + +struct NFTTransferData: CellCodable { + let queryId: UInt64 + let newOwnerAddress: Address + let responseAddress: Address + let forwardAmount: BigUInt + let forwardPayload: Cell? + + func storeTo(builder: Builder) throws { + try builder.store(uint: 0x5fcc3d14, bits: 32) // transfer op + try builder.store(uint: queryId, bits: 64) + try builder.store(AnyAddress(newOwnerAddress)) + try builder.store(AnyAddress(responseAddress)) + try builder.store(bit: false) // null custom_payload + try builder.store(coins: Coins(forwardAmount.magnitude)) + try builder.store(bit: forwardPayload != nil) // forward_payload in this slice - false, separate cell - true + try builder.storeMaybe(ref: forwardPayload) + } + + static func loadFrom(slice: Slice) throws -> NFTTransferData { + try slice.skip(32) + let queryId = try slice.loadUint(bits: 64) + let newOwnerAddress: Address = try slice.loadType() + let responseAddress: Address = try slice.loadType() + try slice.skip(1) + let forwardAmount = try slice.loadCoins().amount + let hasPayloadCell = try slice.loadBoolean() + var forwardPayload: Cell? + if hasPayloadCell, let payloadCell = try slice.loadMaybeRef() { + forwardPayload = payloadCell + } + return NFTTransferData( + queryId: queryId, + newOwnerAddress: newOwnerAddress, + responseAddress: responseAddress, + forwardAmount: forwardAmount, + forwardPayload: forwardPayload) + } +} diff --git a/Source/TonSwift/NFT/NFTTransferMessage.swift b/Source/TonSwift/NFT/NFTTransferMessage.swift new file mode 100644 index 0000000..0ebcc0c --- /dev/null +++ b/Source/TonSwift/NFT/NFTTransferMessage.swift @@ -0,0 +1,32 @@ +// +// NFTTransferMessage.swift +// +// +// Created by Grigory on 25.8.23.. +// + +import Foundation +import BigInt + +public struct NFTTransferMessage { + public static func internalMessage(nftAddress: Address, + nftTransferAmount: BigUInt, + to: Address, + from: Address, + forwardPayload: Cell?) throws -> MessageRelaxed { + let forwardAmount = BigUInt(stringLiteral: "1") + let queryId = UInt64(Date().timeIntervalSince1970) + + let nftTransferData = NFTTransferData( + queryId: queryId, + newOwnerAddress: to, + responseAddress: from, + forwardAmount: forwardAmount, + forwardPayload: forwardPayload) + + return MessageRelaxed.internal( + to: nftAddress, + value: nftTransferAmount, + body: try Builder().store(nftTransferData).endCell()) + } +} diff --git a/Source/TonSwift/Util/SHA256.swift b/Source/TonSwift/Util/SHA256.swift index ffbaa49..aefd146 100644 --- a/Source/TonSwift/Util/SHA256.swift +++ b/Source/TonSwift/Util/SHA256.swift @@ -2,7 +2,7 @@ import CommonCrypto import Foundation extension Data { - func sha256() -> Self { + public func sha256() -> Self { var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) withUnsafeBytes { _ = CC_SHA256($0.baseAddress, CC_LONG(count), &hash) diff --git a/Tests/TonSwiftTests/Jettons/JettonTransferDataTests.swift b/Tests/TonSwiftTests/Jettons/JettonTransferDataTests.swift new file mode 100644 index 0000000..7f03b4e --- /dev/null +++ b/Tests/TonSwiftTests/Jettons/JettonTransferDataTests.swift @@ -0,0 +1,53 @@ +// +// JettonTransferDataTests.swift +// +// +// Created by Grigory on 12.7.23.. +// + +import XCTest +import BigInt +@testable import TonSwift + +final class JettonTransferDataTests: XCTestCase { + + private var transferJettonData: JettonTransferData { + let queryId: UInt64 = 543 + let amount = BigUInt(stringLiteral: "63546") + let toAddress = Address.mock(workchain: 0, seed: "toAddressSeed") + let responseAddress = Address.mock(workchain: 0, seed: "responseAddressSeed") + let forwardAmount = BigUInt(stringLiteral: "789") + let comment = "Hello, this is a comment" + + return JettonTransferData(queryId: queryId, + amount: amount, + toAddress: toAddress, + responseAddress: responseAddress, + forwardAmount: forwardAmount, + comment: comment) + } + + func testJettonTransferDataEncodeAndDecode() throws { + let builder = Builder() + let transferJettonDataCell = try builder.store(transferJettonData).endCell() + let decodedJettonTransferData: JettonTransferData = try Slice(cell: transferJettonDataCell).loadType() + + XCTAssertEqual(transferJettonData.queryId, decodedJettonTransferData.queryId) + XCTAssertEqual(transferJettonData.amount, decodedJettonTransferData.amount) + XCTAssertEqual(transferJettonData.toAddress, decodedJettonTransferData.toAddress) + XCTAssertEqual(transferJettonData.responseAddress, decodedJettonTransferData.responseAddress) + XCTAssertEqual(transferJettonData.forwardAmount, decodedJettonTransferData.forwardAmount) + XCTAssertEqual(transferJettonData.comment, decodedJettonTransferData.comment ) + } + + func testJettonTransferDataEncode() throws { + let builder = Builder() + let transferJettonDataCell = try builder.store(transferJettonData).endCell() + + XCTAssertEqual(try transferJettonDataCell.toString(), + """ + x{0F8A7EA5000000000000021F2F83A8011740C74E876FBD00C1F39161C9DF68563FC469A730CBF932D464EB819239084F001AAD4BD6DAA342C7D03C496083C01AEC31F1A457B8C993C62823E3713E11FD8184062BC_} + x{0000000048656C6C6F2C2074686973206973206120636F6D6D656E74} + """) + } +} diff --git a/Tests/TonSwiftTests/NFT/NFTTransferDataTests.swift b/Tests/TonSwiftTests/NFT/NFTTransferDataTests.swift new file mode 100644 index 0000000..3865add --- /dev/null +++ b/Tests/TonSwiftTests/NFT/NFTTransferDataTests.swift @@ -0,0 +1,61 @@ +// +// NFTTransferDataTests.swift +// +// +// Created by Grigory on 25.8.23.. +// + +import XCTest +import BigInt +@testable import TonSwift + +final class NFTTransferDataTests: XCTestCase { + + private var nftTransferData: NFTTransferData { + get throws { + let queryId: UInt64 = 543 + let newOwnerAddress = Address.mock( + workchain: 0, + seed: "newOwnerAddressSeed" + ) + let responseAddress = Address.mock( + workchain: 0, + seed: "responseAddressSeed" + ) + let forwardAmount = BigUInt(stringLiteral: "05000000") + let comment = "Hello, this is a comment" + let commentCell = try Builder() + .store(int: 0, bits: 32) + .writeSnakeData(Data(comment.utf8)).endCell() + + return NFTTransferData(queryId: queryId, + newOwnerAddress: newOwnerAddress, + responseAddress: responseAddress, + forwardAmount: forwardAmount, + forwardPayload: commentCell) + } + } + + func testJettonTransferDataEncodeAndDecode() throws { + let builder = Builder() + let nftTransferDataCell = try builder.store(nftTransferData).endCell() + let decodedNFTTransferData: NFTTransferData = try Slice(cell: nftTransferDataCell).loadType() + + XCTAssertEqual(try nftTransferData.queryId, decodedNFTTransferData.queryId) + XCTAssertEqual(try nftTransferData.newOwnerAddress, decodedNFTTransferData.newOwnerAddress) + XCTAssertEqual(try nftTransferData.responseAddress, decodedNFTTransferData.responseAddress) + XCTAssertEqual(try nftTransferData.forwardAmount, decodedNFTTransferData.forwardAmount) + XCTAssertEqual(try nftTransferData.forwardPayload, decodedNFTTransferData.forwardPayload) + } + + func testJettonTransferDataEncode() throws { + let builder = Builder() + let nftTransferDataCell = try builder.store(try nftTransferData).endCell() + + XCTAssertEqual(try nftTransferDataCell.toString(), + """ + x{5FCC3D14000000000000021F8006E2451A5FF8C5BEA1DC8505A789427312E714E1783A8DEBF2573D06BADDAD571001AAD4BD6DAA342C7D03C496083C01AEC31F1A457B8C993C62823E3713E11FD8186989681C_} + x{0000000048656C6C6F2C2074686973206973206120636F6D6D656E74} + """) + } +}