Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring #9

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
16 changes: 6 additions & 10 deletions Source/TonSwift/Address/ADNLAddress.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

struct ADNLAddress {
struct ADNLAddress: Equatable {
static func parseFriendly(_ src: String) throws -> ADNLAddress {
if src.count != 55 {
throw TonError.custom("Invalid address")
Expand All @@ -21,9 +21,7 @@ struct ADNLAddress {
}

static func parseRaw(_ src: String) throws -> ADNLAddress {
let data = Data(base64Encoded: src)

return try ADNLAddress(address: data!)
try ADNLAddress(address: Data(base64Encoded: src)!)
}

let address: Data
Expand All @@ -37,22 +35,20 @@ struct ADNLAddress {
}

func toRaw() -> String {
return address.map { String(format: "%02X", $0) }.joined().uppercased()
address.map { String(format: "%02X", $0) }.joined().uppercased()
}

func toString() -> String {
var data = Data([0x2D]) + self.address
var data = Data([0x2D]) + address
let hash = data.crc16()
data = data + hash

return String(data.toBase32().dropFirst())
}

}
// MARK: - Equatable

// MARK: - Equatable
extension ADNLAddress: Equatable {
static func == (lhs: ADNLAddress, rhs: ADNLAddress) -> Bool {
return lhs.address == rhs.address
lhs.address == rhs.address
}
}
93 changes: 30 additions & 63 deletions Source/TonSwift/Address/Address.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public struct Address: Hashable, Codable {
public struct Address: Hashable, Codable, Equatable, CellCodable, StaticSize {
public let workchain: Int8
public let hash: Data

Expand All @@ -12,21 +12,17 @@ public struct Address: Hashable, Codable {

/// Generates a test address
public static func mock(workchain: Int8, seed: String) -> Self {
return Address(workchain: workchain, hash: Data(seed.utf8).sha256())
Address(workchain: workchain, hash: Data(seed.utf8).sha256())
}

/// Parses address from any format
public static func parse(_ source: String) throws -> Address {
if source.firstIndex(of: ":") == nil {
return try FriendlyAddress(string: source).address
} else {
return try parse(raw: source)
}
source.firstIndex(of: ":") == nil ? try FriendlyAddress(string: source).address : try parse(raw: source)
}

/// Initializes address from the raw format `<workchain>:<hash>` (decimal workchain, hex-encoded hash part)
public static func parse(raw: String) throws -> Address {
let parts = raw.split(separator: ":");
let parts = raw.split(separator: ":")
guard parts.count == 2 else {
throw TonError.custom("Raw address is malformed: should be in the form `<workchain number>:<hex>`")
}
Expand All @@ -41,62 +37,57 @@ public struct Address: Hashable, Codable {

/// Returns raw format of the address: `<workchain>:<hash>` (decimal workchain, hex-encoded hash part)
public func toRaw() -> String {
return "\(workchain):\(hash.hexString())"
"\(workchain):\(hash.hexString())"
}

/// Returns raw format of the address: `<workchain>:<hash>` (decimal workchain, hex-encoded hash part)
public func toFriendly(testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> FriendlyAddress {
return FriendlyAddress(address: self, testOnly: testOnly, bounceable: bounceable)
FriendlyAddress(address: self, testOnly: testOnly, bounceable: bounceable)
}

/// Shortcut for constructing FriendlyAddress with all the options.
public func toString(urlSafe: Bool = true, testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> String {
return self.toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe)
toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe)
}
}

// MARK: - Equatable
extension Address: Equatable {
// MARK: - Equatable

public static func == (lhs: Address, rhs: Address) -> Bool {
if lhs.workchain != rhs.workchain {
return false
}

return lhs.hash == rhs.hash
lhs.workchain != rhs.workchain ? false : lhs.hash == rhs.hash
}
}

/// ```
/// anycast_info$_ depth:(#<= 30) { depth >= 1 }
/// rewrite_pfx:(bits depth) = Anycast;
/// addr_std$10 anycast:(Maybe Anycast)
/// workchain_id:int8
/// address:bits256 = MsgAddressInt;
/// addr_var$11 anycast:(Maybe Anycast)
/// addr_len:(## 9)
/// workchain_id:int32
/// address:(bits addr_len) = MsgAddressInt;
/// ```
extension Address: CellCodable, StaticSize {
// MARK: - CellCodable, StaticSize
/// ```
/// anycast_info$_ depth:(#<= 30) { depth >= 1 }
/// rewrite_pfx:(bits depth) = Anycast;
/// addr_std$10 anycast:(Maybe Anycast)
/// workchain_id:int8
/// address:bits256 = MsgAddressInt;
/// addr_var$11 anycast:(Maybe Anycast)
/// addr_len:(## 9)
/// workchain_id:int32
/// address:(bits addr_len) = MsgAddressInt;
/// ```

public static var bitWidth: Int = 267

public func storeTo(builder b: Builder) throws {
try b.store(uint: 2, bits: 2) // $10
try b.store(uint: 0, bits: 1)
try b.store(int: self.workchain, bits: 8)
try b.store(data: self.hash)
public func storeTo(builder: Builder) throws {
try builder
.store(uint: 2, bits: 2)
.store(uint: 0, bits: 1)
.store(int: workchain, bits: 8)
.store(data: hash)
}

public static func loadFrom(slice: Slice) throws -> Address {
return try slice.tryLoad { s in
try slice.tryLoad { s in
let type = try s.loadUint(bits: 2)
if type != 2 {
throw TonError.custom("Unsupported address type: expecting internal address `addr_std$10`")
}

// No Anycast supported
let anycastPrefix = try s.loadUint(bits: 1);
let anycastPrefix = try s.loadUint(bits: 1)
if anycastPrefix != 0 {
throw TonError.custom("Invalid address: anycast not supported")
}
Expand All @@ -109,27 +100,3 @@ extension Address: CellCodable, StaticSize {
}
}
}

/// The most compact address encoding that's often used within smart contracts: workchain + hash.
public struct CompactAddress: Hashable, CellCodable, StaticSize {
public static var bitWidth: Int = 8 + 256
public let inner: Address

init(_ inner: Address) {
self.inner = inner
}

public func storeTo(builder b: Builder) throws {
try b.store(int: inner.workchain, bits: 8)
try b.store(data: inner.hash)
}

public static func loadFrom(slice: Slice) throws -> CompactAddress {
return try slice.tryLoad { s in
let wc = Int8(try s.loadInt(bits: 8))
let hash = try s.loadBytes(32)
return CompactAddress(Address(workchain: wc, hash: hash))
}
}
}

24 changes: 12 additions & 12 deletions Source/TonSwift/Address/AnyAddress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Foundation
/// _ _:MsgAddressExt = MsgAddress;
/// ```
///
enum AnyAddress {
enum AnyAddress: CellCodable {
case none
case internalAddr(Address)
case externalAddr(ExternalAddress)
Expand Down Expand Up @@ -47,23 +47,23 @@ enum AnyAddress {
/// Converts to an optional internal address. Throws error if it is an external address.
public func asInternal() throws -> Address? {
switch self {
case .none: return nil;
case .internalAddr(let addr): return addr;
case .none: return nil
case .internalAddr(let addr): return addr
case .externalAddr(_): throw TonError.custom("Expected internal address")
}
}

/// Converts to an external address. Throws error if it is an internal address.
public func asExternal() throws -> ExternalAddress? {
switch self {
case .none: return nil;
case .none: return nil
case .internalAddr(_): throw TonError.custom("Expected external address")
case .externalAddr(let addr): return addr;
case .externalAddr(let addr): return addr
}
}
}

extension AnyAddress: CellCodable {
// MARK: - CellCodable

func storeTo(builder: Builder) throws {
switch self {
case .none:
Expand All @@ -82,14 +82,14 @@ extension AnyAddress: CellCodable {
let type = try slice.preloadUint(bits: 2)
switch type {
case 0:
try slice.skip(2);
return .none;
try slice.skip(2)
return .none
case 1:
return .externalAddr(try slice.loadType());
return .externalAddr(try slice.loadType())
case 2,3:
return .internalAddr(try slice.loadType());
return .internalAddr(try slice.loadType())
default:
throw TonError.custom("Unreachable error");
throw TonError.custom("Unreachable error")
}
}
}
32 changes: 32 additions & 0 deletions Source/TonSwift/Address/CompactAddress.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// CompactAddress.swift
//
//
// Created by Kirill Kirilenko on 20/07/2023.
//

import Foundation

/// The most compact address encoding that's often used within smart contracts: workchain + hash.
public struct CompactAddress: Hashable, CellCodable, StaticSize {
public static var bitWidth: Int = 8 + 256
public let inner: Address

init(_ inner: Address) {
self.inner = inner
}

public func storeTo(builder: Builder) throws {
try builder
.store(int: inner.workchain, bits: 8)
.store(data: inner.hash)
}

public static func loadFrom(slice: Slice) throws -> CompactAddress {
try slice.tryLoad { s in
let wc = Int8(try s.loadInt(bits: 8))
let hash = try s.loadBytes(32)
return CompactAddress(Address(workchain: wc, hash: hash))
}
}
}
18 changes: 8 additions & 10 deletions Source/TonSwift/Address/ExternalAddress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,30 @@ import Foundation
/// ```
/// addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt;
/// ```
public struct ExternalAddress {
public struct ExternalAddress: CellCodable {
private(set) var value: Bitstring

public init(value: Bitstring) {
self.value = value
}

public func toString() -> String {
return "External<\(value.length):\(value.toString())>"
"External<\(value.length):\(value.toString())>"
}

public static func mock(seed: String) throws -> Self {
let value = Bitstring(data: Data(seed.utf8).sha256())
return ExternalAddress(value: value)
ExternalAddress(value: Bitstring(data: Data(seed.utf8).sha256()))
}
}

extension ExternalAddress: CellCodable {
public func storeTo(builder: Builder) throws {
try builder.store(uint: 1, bits: 2)
try builder.store(uint: self.value.length, bits: 9)
try builder.store(bits: self.value)
try builder
.store(uint: 1, bits: 2)
.store(uint: value.length, bits: 9)
.store(bits: value)
}

public static func loadFrom(slice: Slice) throws -> ExternalAddress {
return try slice.tryLoad { s in
try slice.tryLoad { s in
let type = try s.loadUint(bits: 2)
if type != 1 {
throw TonError.custom("Invalid ExternalAddress")
Expand Down
10 changes: 5 additions & 5 deletions Source/TonSwift/Address/FriendlyAddress.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

/// By default, addresses are bounceable for safety of TON transfers.
public let BounceableDefault = true;
public let BounceableDefault = true

let bounceableTag: UInt8 = 0x11
let nonBounceableTag: UInt8 = 0x51
Expand All @@ -13,8 +13,8 @@ public struct FriendlyAddress: Codable {
public let isBounceable: Bool
public let address: Address

var workchain: Int8 { return self.address.workchain }
var hash: Data { return self.address.hash }
var workchain: Int8 { address.workchain }
var hash: Data { address.hash }

init(string: String) throws {
// Convert from url-friendly to true base64
Expand Down Expand Up @@ -76,10 +76,10 @@ public struct FriendlyAddress: Codable {
}

var wcByte: UInt8
if self.address.workchain == -1 {
if address.workchain == -1 {
wcByte = UInt8.max
} else {
wcByte = UInt8(self.workchain)
wcByte = UInt8(workchain)
}

var addr = Data(count: 34)
Expand Down
Loading