This repository has been archived by the owner on Dec 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add exchange state machine * Fix tsdoc warnings * lint * Changeset * Additional tests * Fix
- Loading branch information
Diane Huxley
authored
Feb 13, 2024
1 parent
629f0c7
commit 589edc3
Showing
5 changed files
with
471 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@tbdex/protocol": patch | ||
--- | ||
|
||
Add exchange state machine |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { Close, Order, OrderStatus, Quote, Rfq } from './message-kinds/index.js' | ||
import { Message } from './message.js' | ||
import { MessageKind } from './types.js' | ||
|
||
/** | ||
* State-machine for validating the order and metadata of Tbdex messages in an exchange. | ||
* | ||
* This state-machine does not validate the {@link Message.signature} or {@link Message.data} | ||
* of messages in the exchange. | ||
* | ||
* Either add messages in order one at a time using {@link Exchange.addNextMessage}, | ||
* or add a list of unsorted messages in an exchange using {@link Exchange.addMessages} | ||
* | ||
* @beta | ||
*/ | ||
export class Exchange { | ||
/** Message sent by Alice to PFI to request a quote */ | ||
rfq: Rfq | undefined | ||
/** Message sent by the PFI in response to an RFQ */ | ||
quote: Quote | undefined | ||
/** Message sent by Alice to the PFI to accept a quote*/ | ||
order: Order | undefined | ||
/** Message sent by the PFI to Alice to convet the current status of the order */ | ||
orderstatus: OrderStatus | undefined | ||
/** Message sent by either the PFI or Alice to terminate an exchange */ | ||
close: Close | undefined | ||
|
||
constructor() {} | ||
|
||
/** | ||
* Add a list of unsorted messages to an exchange. | ||
* @param messages - An unsorted array of Tbdex messages in a given exchange | ||
*/ | ||
addMessages(messages: Message[]): void { | ||
// Sort with earliest dateCreated first | ||
const sortedMessages = messages.sort((m1, m2) => { | ||
const time1 = new Date(m1.metadata.createdAt).getTime() | ||
const time2 = new Date(m2.metadata.createdAt).getTime() | ||
return time1 - time2 | ||
}) | ||
|
||
for (const message of sortedMessages) { | ||
this.addNextMessage(message) | ||
} | ||
} | ||
|
||
/** | ||
* Add the next message in the exchange | ||
* @param message - The next allowed message in the exchange | ||
* @throws if message is not a valid next message. See {@link Exchange.isValidNext} | ||
*/ | ||
addNextMessage(message: Message): void { | ||
if (!this.isValidNext(message.metadata.kind)) { | ||
throw new Error( | ||
`Could not add message (${message.metadata.id}) to exchange because ${message.metadata.kind} ` + | ||
`is not a valid next message` | ||
) | ||
} | ||
|
||
if (this.exchangeId !== undefined && message.metadata.exchangeId !== this.exchangeId) { | ||
throw new Error( | ||
`Could not add message with id ${message.metadata.id} to exchange because it does not have matching ` + | ||
`exchange id ${this.exchangeId}` | ||
) | ||
} | ||
|
||
if (message.isRfq()) { | ||
this.rfq = message | ||
} else if (message.isQuote()) { | ||
this.quote = message | ||
} else if (message.isClose()) { | ||
this.close = message | ||
} else if (message.isOrder()) { | ||
this.order = message | ||
} else if (message.isOrderStatus()) { | ||
this.orderstatus = message | ||
} else { | ||
// Unreachable | ||
throw new Error('Unrecognized message kind') | ||
} | ||
} | ||
|
||
/** | ||
* Determines if the message kind is a valid next message in the current exchange | ||
* @param messageKind - the kind of TBDex message | ||
* @returns true if the next message in the exchange may have kind messageKind, false otherwise | ||
*/ | ||
isValidNext(messageKind: MessageKind): boolean { | ||
const validNext = this.latestMessage?.validNext ?? new Set<MessageKind>(['rfq']) | ||
return validNext.has(messageKind) | ||
} | ||
|
||
/** | ||
* Latest message in an exchange if there are any messages currently | ||
*/ | ||
get latestMessage(): Message | undefined { | ||
return this.close ?? | ||
this.orderstatus ?? | ||
this.order ?? | ||
this.quote ?? | ||
this.rfq | ||
} | ||
|
||
/** | ||
* The exchangeId of all messages in the Exchange | ||
*/ | ||
get exchangeId(): string | undefined { | ||
return this.rfq?.metadata?.exchangeId | ||
} | ||
|
||
/** | ||
* A sorted list of messages currently in the exchange. | ||
*/ | ||
get messages(): Message[] { | ||
const allPossibleMessages: (Message | undefined)[] = [ | ||
this.rfq, | ||
this.quote, | ||
this.order, | ||
this.orderstatus, | ||
this.close | ||
] | ||
return allPossibleMessages.filter((message): message is Message => message !== undefined) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.