Skip to content

Commit

Permalink
feat: add OrderInstructions (#99)
Browse files Browse the repository at this point in the history
* add `OrderInstructions` message

* remove `PaymentInstruction` from `Quote`

* add `createOrderInstructions` in `DevTools`

* remove `PaymentInstruction` checks in `QuoteTests`

* add `OrderInstructionsTests`
  • Loading branch information
ethan-tbd authored Aug 1, 2024
1 parent b28cbe5 commit 1234102
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 35 deletions.
50 changes: 45 additions & 5 deletions Sources/tbDEX/Protocol/DevTools.swift
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,8 @@ enum DevTools {
expiresAt: expiresAt,
payin: .init(
currencyCode: "USD",
amount: "1.00",
paymentInstruction: .init(
link: "https://example.com",
instruction: "test instruction"
)
amount: "1.00"

),
payout: .init(
currencyCode: "AUD",
Expand Down Expand Up @@ -195,6 +192,49 @@ enum DevTools {
}
}

/// Creates a mock `OrderInstructions`. Optionally override the `OrderInstructionsData`
/// - Parameters:
/// - from: The DID the `OrderInstructions` should be from. Included in the metadata.
/// - to: The DID the `OrderInstructions` should be sent to. Included in the metadata.
/// - exchangeID: The exchangeID of the associated exchange. Included in the metadata.
/// - protocol: Optional. The protocol version to use if different from the default. Included in the metadata.
/// - Returns: The `OrderInstructions`
static func createOrderInstructions(
from: String,
to: String,
exchangeID: String = "exchange_123",
data: OrderInstructionsData? = nil,
protocol: String? = nil
) -> OrderInstructions {
let orderInstructionsData = data ?? OrderInstructionsData(
payin: .init(
link: "https://example.com",
instruction: "test instruction"
),
payout: .init(
link: "https://example.com",
instruction: "test instruction"
)
)

if let `protocol` = `protocol` {
return OrderInstructions(
from: from,
to: to,
exchangeID: exchangeID,
data: orderInstructionsData,
protocol: `protocol`
)
} else {
return OrderInstructions(
from: from,
to: to,
exchangeID: exchangeID,
data: orderInstructionsData
)
}
}

/// Creates a mock `OrderStatus`. Optionally override the `OrderStatusData`
/// - Parameters:
/// - from: The DID the `OrderStatus` should be from. Included in the metadata.
Expand Down
1 change: 1 addition & 0 deletions Sources/tbDEX/Protocol/Models/Message.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public enum MessageKind: String, Codable {
case close
case quote
case order
case orderInstructions = "orderinstructions"
case orderStatus = "orderstatus"
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/tbDEX/Protocol/Models/Messages/AnyMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Foundation
public enum AnyMessage {
case close(Close)
case order(Order)
case orderInstructions(OrderInstructions)
case orderStatus(OrderStatus)
case quote(Quote)
case rfq(RFQ)
Expand Down Expand Up @@ -58,6 +59,8 @@ extension AnyMessage: Decodable {
self = .close(try container.decode(Close.self))
case .order:
self = .order(try container.decode(Order.self))
case .orderInstructions:
self = .orderInstructions(try container.decode(OrderInstructions.self))
case .orderStatus:
self = .orderStatus(try container.decode(OrderStatus.self))
case .quote:
Expand Down
50 changes: 50 additions & 0 deletions Sources/tbDEX/Protocol/Models/Messages/OrderInstructions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation

public typealias OrderInstructions = Message<OrderInstructionsData>

/// Data that makes up a OrderInstructions Message.
///
/// [Specification Reference](https://github.com/TBD54566975/tbdex/blob/main/specs/protocol/README.md#orderinstructions)
public struct OrderInstructionsData: MessageData {

/// The amount of payin currency that the PFI will receive
public let payin: PaymentInstruction

/// The amount of payout currency that Alice will receive
public let payout: PaymentInstruction

/// Returns the MessageKind of quote
public func kind() -> MessageKind {
return .orderInstructions
}

/// Default Initializer
public init(
payin: PaymentInstruction,
payout: PaymentInstruction
) {
self.payin = payin
self.payout = payout
}
}

/// Instruction about how to pay or be paid by the PFI
///
/// [Specification Reference](https://github.com/TBD54566975/tbdex/tree/main/specs/protocol#paymentinstruction)
public struct PaymentInstruction: Codable, Equatable {

/// Link to allow Alice to pay PFI, or be paid by the PFI
public let link: String?

/// Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI
public let instruction: String?

/// Default Initializer
public init(
link: String? = nil,
instruction: String? = nil
) {
self.link = link
self.instruction = instruction
}
}
28 changes: 1 addition & 27 deletions Sources/tbDEX/Protocol/Models/Messages/Quote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,40 +47,14 @@ public struct QuoteDetails: Codable, Equatable {
/// The amount paid in fees
public let fee: String?

/// Object that describes how to pay the PFI, and how to get paid by the PFI (e.g. BTC address, payment link)
public let paymentInstruction: PaymentInstruction?

/// Default Initializer
public init(
currencyCode: String,
amount: String,
fee: String? = nil,
paymentInstruction: PaymentInstruction? = nil
fee: String? = nil
) {
self.currencyCode = currencyCode
self.amount = amount
self.fee = fee
self.paymentInstruction = paymentInstruction
}
}

/// Instruction about how to pay or be paid by the PFI
///
/// [Specification Reference](https://github.com/TBD54566975/tbdex/tree/main/specs/protocol#paymentinstruction)
public struct PaymentInstruction: Codable, Equatable {

/// Link to allow Alice to pay PFI, or be paid by the PFI
public let link: String?

/// Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI
public let instruction: String?

/// Default Initializer
public init(
link: String? = nil,
instruction: String? = nil
) {
self.link = link
self.instruction = instruction
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Web5
import XCTest

@testable import tbDEX

final class OrderInstructionsTests: XCTestCase {

let did = try! DIDJWK.create(keyManager: InMemoryKeyManager())
let pfi = try! DIDJWK.create(keyManager: InMemoryKeyManager())

func test_init() {
let orderInstructions = DevTools.createOrderInstructions(from: pfi.uri, to: did.uri)

XCTAssertEqual(orderInstructions.metadata.id.prefix, "orderinstructions")
XCTAssertEqual(orderInstructions.metadata.from, pfi.uri)
XCTAssertEqual(orderInstructions.metadata.to, did.uri)
XCTAssertEqual(orderInstructions.metadata.exchangeID, "exchange_123")

XCTAssertEqual(orderInstructions.data.payin.link, "https://example.com")
XCTAssertEqual(orderInstructions.data.payin.instruction, "test instruction")

XCTAssertEqual(orderInstructions.data.payout.link, "https://example.com")
XCTAssertEqual(orderInstructions.data.payout.instruction, "test instruction")
}

func test_verifySuccess() async throws {
var orderInstructions = DevTools.createOrderInstructions(from: pfi.uri, to: did.uri)
try orderInstructions.sign(did: pfi)

let isValid = try await orderInstructions.verify()
XCTAssertTrue(isValid)
}

func test_verifyWithoutSigningFailure() async throws {
let orderInstructions = DevTools.createOrderInstructions(from: pfi.uri, to: did.uri)

await XCTAssertThrowsErrorAsync(try await orderInstructions.verify())
}
}
3 changes: 0 additions & 3 deletions Tests/tbDEXTests/Protocol/Models/Messages/QuoteTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,10 @@ final class QuoteTests: XCTestCase {
XCTAssertEqual(quote.data.payin.currencyCode, "USD")
XCTAssertEqual(quote.data.payin.amount, "1.00")
XCTAssertNil(quote.data.payin.fee)
XCTAssertEqual(quote.data.payin.paymentInstruction?.link, "https://example.com")
XCTAssertEqual(quote.data.payin.paymentInstruction?.instruction, "test instruction")

XCTAssertEqual(quote.data.payout.currencyCode, "AUD")
XCTAssertEqual(quote.data.payout.amount, "2.00")
XCTAssertEqual(quote.data.payout.fee, "0.50")
XCTAssertNil(quote.data.payout.paymentInstruction)
}

func test_verifySuccess() async throws {
Expand Down

0 comments on commit 1234102

Please sign in to comment.