diff --git a/hosted/json-schemas/message.schema.json b/hosted/json-schemas/message.schema.json index 36c4880d..3407787c 100644 --- a/hosted/json-schemas/message.schema.json +++ b/hosted/json-schemas/message.schema.json @@ -16,7 +16,7 @@ }, "kind": { "type": "string", - "enum": ["rfq", "quote", "order", "orderstatus", "close", "cancel"], + "enum": ["rfq", "quote", "order", "orderstatus", "close", "cancel", "orderinstructions"], "description": "The message kind (e.g. rfq, quote)" }, "id": { diff --git a/hosted/json-schemas/orderinstructions.schema.json b/hosted/json-schemas/orderinstructions.schema.json new file mode 100644 index 00000000..ec711cde --- /dev/null +++ b/hosted/json-schemas/orderinstructions.schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://tbdex.dev/orderinstructions.schema.json", + "type": "object", + "additionalProperties": false, + "properties": { + "payin": { + "$ref": "#/definitions/PaymentInstruction" + }, + "payout": { + "$ref": "#/definitions/PaymentInstruction" + } + }, + "definitions": { + "PaymentInstruction": { + "type": "object", + "additionalProperties": false, + "properties": { + "link": { + "type": "string", + "description": "Link to allow Alice to pay PFI, or be paid by the PFI" + }, + "instruction": { + "type": "string", + "description": "Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI" + } + } + } + }, + "required": ["payin", "payout"] +} diff --git a/hosted/json-schemas/quote.schema.json b/hosted/json-schemas/quote.schema.json index e701bd9e..a4450e4d 100644 --- a/hosted/json-schemas/quote.schema.json +++ b/hosted/json-schemas/quote.schema.json @@ -21,26 +21,9 @@ "total": { "$ref": "definitions.json#/definitions/decimalString", "description": "The total amount of currency to be paid in or paid out. It is always a sum of subtotal and fee" - }, - "paymentInstruction": { - "$ref": "#/definitions/PaymentInstruction" } }, "required": ["currencyCode", "subtotal", "total"] - }, - "PaymentInstruction": { - "type": "object", - "additionalProperties": false, - "properties": { - "link": { - "type": "string", - "description": "Link to allow Alice to pay PFI, or be paid by the PFI" - }, - "instruction": { - "type": "string", - "description": "Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI" - } - } } }, "type": "object", diff --git a/hosted/test-vectors/protocol/vectors/parse-orderinstructions.json b/hosted/test-vectors/protocol/vectors/parse-orderinstructions.json new file mode 100644 index 00000000..56cdb736 --- /dev/null +++ b/hosted/test-vectors/protocol/vectors/parse-orderinstructions.json @@ -0,0 +1,27 @@ +{ + "description": "OrderInstructions parses from string", + "input": "{\"metadata\":{\"from\":\"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IlJnenp4VEhEWkhua2l1dXg5RWE0YktZS0ttSzR5QkRYRnlSSGUtLVQ2Y2sifQ\",\"to\":\"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IndiTTc4VzAtcXhtM0wxOWZfMkYtZ1FWOUhWNkEtcERwOVJyWUtNN0NCZTgifQ\",\"kind\":\"orderinstructions\",\"id\":\"orderinstructions_01j3ers909e91v1m52p1pg4dwt\",\"exchangeId\":\"rfq_01j3ers909e91a1e1cafmj9b9z\",\"createdAt\":\"2024-07-23T03:05:00Z\",\"protocol\":\"1.0\"},\"data\":{\"payin\":{\"link\":\"http://example.com/payin/123\",\"instruction\":\"deducted from stored balance\"},\"payout\":{\"link\":\"http://example.com/payout/123\",\"instruction\":\"sent to your bank account\"}},\"signature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbEpuZW5wNFZFaEVXa2h1YTJsMWRYZzVSV0UwWWt0WlMwdHRTelI1UWtSWVJubFNTR1V0TFZRMlkyc2lmUSMwIn0..FIa-a_6a_eKEuM6gAcWp9VPKge_x1UFnPhoT6fdsq0MxgYBRlPPs1fzNqaKD7U_M4icjZ2x4lWR10GLkBy-gCA\"}", + "output": { + "metadata": { + "from": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IlJnenp4VEhEWkhua2l1dXg5RWE0YktZS0ttSzR5QkRYRnlSSGUtLVQ2Y2sifQ", + "to": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IndiTTc4VzAtcXhtM0wxOWZfMkYtZ1FWOUhWNkEtcERwOVJyWUtNN0NCZTgifQ", + "kind": "orderinstructions", + "id": "orderinstructions_01j3ers909e91v1m52p1pg4dwt", + "exchangeId": "rfq_01j3ers909e91a1e1cafmj9b9z", + "createdAt": "2024-07-23T03:05:00Z", + "protocol": "1.0" + }, + "data": { + "payin": { + "link": "http://example.com/payin/123", + "instruction": "deducted from stored balance" + }, + "payout": { + "link": "http://example.com/payout/123", + "instruction": "sent to your bank account" + } + }, + "signature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbEpuZW5wNFZFaEVXa2h1YTJsMWRYZzVSV0UwWWt0WlMwdHRTelI1UWtSWVJubFNTR1V0TFZRMlkyc2lmUSMwIn0..FIa-a_6a_eKEuM6gAcWp9VPKge_x1UFnPhoT6fdsq0MxgYBRlPPs1fzNqaKD7U_M4icjZ2x4lWR10GLkBy-gCA" + }, + "error": false +} diff --git a/hosted/test-vectors/protocol/vectors/parse-quote.json b/hosted/test-vectors/protocol/vectors/parse-quote.json index e8e52538..0cb8e1da 100644 --- a/hosted/test-vectors/protocol/vectors/parse-quote.json +++ b/hosted/test-vectors/protocol/vectors/parse-quote.json @@ -1,39 +1,33 @@ { "description": "Quote parses from string", - "input": "{\"metadata\": {\"from\": \"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im83eG85MkxXOWNiZGUtOWRMZUE1ZFhDVjBneUdWVnVtb0xfZVlmVFhtWGsifQ\",\"to\": \"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Ii12V195SDFMMWkzUW5vVlNGcXNrMDRGWGd4YVhhRGtHOEV1cF96MmxtWlUifQ\",\"kind\": \"quote\",\"id\": \"quote_01j2f22daeefet5g9ynxg5stm3\",\"exchangeId\": \"rfq_01j2f22daeefeaaqbp61thnxpb\",\"createdAt\": \"2024-07-10T19:31:34Z\",\"protocol\": \"1.0\"},\"data\": {\"expiresAt\": \"2024-07-10T19:31:34Z\",\"payoutUnitsPerPayinUnit\": \"16.665\",\"payin\": {\"currencyCode\": \"USD\",\"subtotal\": \"10\",\"fee\": \"0\",\"total\": \"10\",\"paymentInstruction\": {\"instruction\": \"use link provided\"}},\"payout\": {\"currencyCode\": \"MXN\",\"subtotal\": \"500\",\"fee\": \"0\",\"total\": \"500\",\"paymentInstruction\": {\"instruction\": \"SPEI transfer\"}}},\"signature\": \"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbTgzZUc4NU1reFhPV05pWkdVdE9XUk1aVUUxWkZoRFZqQm5lVWRXVm5WdGIweGZaVmxtVkZodFdHc2lmUSMwIn0..SYMZMQwrThsrNHXxHnXoiozCyvtePVOy3kHzQ0Pj3LYYf0h-l6PC2GLblWEtGVJrz27Ct_VdqUnOvm7nF6NLAA\"}", + "input": "{\"metadata\":{\"from\":\"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6ImdJVUR0dFBhaDFhaGxSVnA5dnhpNkFsTVZtWDBEcGF4Y0hlbWU0TE1jRGMifQ\",\"to\":\"did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im8yek5wb284bFVhbGVfZXcwcE9la09maWdic3IwM1F2VUNCNkxwVkZXblUifQ\",\"kind\":\"quote\",\"id\":\"quote_01j3ern5tcf6zs8frq64wtbe2b\",\"exchangeId\":\"rfq_01j3ern5tcf6z81ch2e6tm1sws\",\"createdAt\":\"2024-07-23T03:02:45Z\",\"protocol\":\"1.0\"},\"data\":{\"expiresAt\":\"2024-07-23T03:02:45Z\",\"payoutUnitsPerPayinUnit\":\"16.665\",\"payin\":{\"currencyCode\":\"USD\",\"subtotal\":\"10\",\"fee\":\"0.1\",\"total\":\"10.1\"},\"payout\":{\"currencyCode\":\"MXN\",\"subtotal\":\"500\",\"fee\":\"0\",\"total\":\"500\"}},\"signature\":\"eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbWRKVlVSMGRGQmhhREZoYUd4U1ZuQTVkbmhwTmtGc1RWWnRXREJFY0dGNFkwaGxiV1UwVEUxalJHTWlmUSMwIn0..ceuURLX2zdrAoUHlsQ6jfgWj4ika0oq4uxxJ7w_y7vUK5sizV0AzqMEmit469MjT5-cjCJe6oXzES3pqgnHvAQ\"}", "output": { "metadata": { - "from": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im83eG85MkxXOWNiZGUtOWRMZUE1ZFhDVjBneUdWVnVtb0xfZVlmVFhtWGsifQ", - "to": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Ii12V195SDFMMWkzUW5vVlNGcXNrMDRGWGd4YVhhRGtHOEV1cF96MmxtWlUifQ", + "from": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6ImdJVUR0dFBhaDFhaGxSVnA5dnhpNkFsTVZtWDBEcGF4Y0hlbWU0TE1jRGMifQ", + "to": "did:jwk:eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im8yek5wb284bFVhbGVfZXcwcE9la09maWdic3IwM1F2VUNCNkxwVkZXblUifQ", "kind": "quote", - "id": "quote_01j2f22daeefet5g9ynxg5stm3", - "exchangeId": "rfq_01j2f22daeefeaaqbp61thnxpb", - "createdAt": "2024-07-10T19:31:34Z", + "id": "quote_01j3ern5tcf6zs8frq64wtbe2b", + "exchangeId": "rfq_01j3ern5tcf6z81ch2e6tm1sws", + "createdAt": "2024-07-23T03:02:45Z", "protocol": "1.0" }, "data": { - "expiresAt": "2024-07-10T19:31:34Z", + "expiresAt": "2024-07-23T03:02:45Z", "payoutUnitsPerPayinUnit": "16.665", "payin": { "currencyCode": "USD", "subtotal": "10", - "fee": "0", - "total": "10", - "paymentInstruction": { - "instruction": "use link provided" - } + "fee": "0.1", + "total": "10.1" }, "payout": { "currencyCode": "MXN", "subtotal": "500", "fee": "0", - "total": "500", - "paymentInstruction": { - "instruction": "SPEI transfer" - } + "total": "500" } }, - "signature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbTgzZUc4NU1reFhPV05pWkdVdE9XUk1aVUUxWkZoRFZqQm5lVWRXVm5WdGIweGZaVmxtVkZodFdHc2lmUSMwIn0..SYMZMQwrThsrNHXxHnXoiozCyvtePVOy3kHzQ0Pj3LYYf0h-l6PC2GLblWEtGVJrz27Ct_VdqUnOvm7nF6NLAA" + "signature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbWRKVlVSMGRGQmhhREZoYUd4U1ZuQTVkbmhwTmtGc1RWWnRXREJFY0dGNFkwaGxiV1UwVEUxalJHTWlmUSMwIn0..ceuURLX2zdrAoUHlsQ6jfgWj4ika0oq4uxxJ7w_y7vUK5sizV0AzqMEmit469MjT5-cjCJe6oXzES3pqgnHvAQ" }, "error": false -} \ No newline at end of file +} diff --git a/specs/protocol/README.md b/specs/protocol/README.md index 81727163..37c90d96 100644 --- a/specs/protocol/README.md +++ b/specs/protocol/README.md @@ -50,10 +50,12 @@ Version: Draft - [Example RFQ](#example-rfq) - [`Quote`](#quote) - [`QuoteDetails`](#quotedetails) - - [`PaymentInstruction`](#paymentinstruction) - [Example Quote](#example-quote) - [`Order`](#order) - [Example Order](#example-order) + - [`OrderInstructions`](#orderinstructions) + - [`PaymentInstruction`](#paymentinstruction) + - [Example Quote](#example-quote-1) - [`OrderStatus`](#orderstatus) - [`Status`](#status) - [`Close`](#close) @@ -536,19 +538,12 @@ This table enumerates the structure of `PrivateData` #### `QuoteDetails` -| field | data type | required | description | -| -------------------- | ------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------- | -| `currencyCode` | string | Y | ISO 4217 currency code string | -| `subtotal` | [`DecimalString`](#decimalstring) | Y | The amount of currency paid for the exchange, **excluding** fees | -| `fee` | [`DecimalString`](#decimalstring) | N | The amount of currency paid in fees | -| `total` | [`DecimalString`](#decimalstring) | Y | The total amount of currency to be paid in or paid out. It is always a sum of `subtotal` and `fee` | -| `paymentInstruction` | [`PaymentInstruction`](#paymentinstruction) | N | Object that describes how to pay the PFI, and how to get paid by the PFI (e.g. BTC address, payment link) | - -#### `PaymentInstruction` -| field | data type | required | description | -| ------------- | --------- | -------- | ------------------------------------------------------------------------- | -| `link` | String | N | Link to allow Alice to pay PFI, or be paid by the PFI | -| `instruction` | String | N | Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI | +| field | data type | required | description | +| -------------- | --------------------------------- | -------- | -------------------------------------------------------------------------------------------------- | +| `currencyCode` | string | Y | ISO 4217 currency code string | +| `subtotal` | [`DecimalString`](#decimalstring) | Y | The amount of currency paid for the exchange, **excluding** fees | +| `fee` | [`DecimalString`](#decimalstring) | N | The amount of currency paid in fees | +| `total` | [`DecimalString`](#decimalstring) | Y | The total amount of currency to be paid in or paid out. It is always a sum of `subtotal` and `fee` | #### Example Quote ```json @@ -570,17 +565,11 @@ This table enumerates the structure of `PrivateData` "subtotal": "200.00", "fee": "0.20", "total": "200.20", - "paymentInstruction": { - "link": "https://example-pfi.com/payin?currency=usd&amount=200.20" - } }, "payout": { "currencyCode": "BTC", "subtotal": "0.000016", "total": "0.000016", - "paymentInstruction": { - "instruction": "BTC will be paid to the provided BTC address" - } } }, "signature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa29yZUMxVVJNUUVWS204d3h6aFZGbnRMenZQWFlidEg4Q0VGTktVY1NrTFdUI3o2TWtvcmVDMVVSTVFFVkttOHd4emhWRm50THp2UFhZYnRIOENFRk5LVWNTa0xXVCJ9..R_BBKJoWifPFh10GJ1ij2gCCxND1CdzKbiOgPCIha__0GvRy0rHYCi18-TY7jNARaQ94RHXHYIsCRm2MuOPACw" @@ -607,6 +596,48 @@ This table enumerates the structure of `PrivateData` } ``` + +### `OrderInstructions` +> PFI -> Alice: "Here's how to pay us, and how to let us pay you." + +| field | data type | required | description | +| -------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------- | +| `payin` | [`PaymentInstruction`](#PaymentInstruction) | Y | Object that describes how to pay the PFI (e.g. BTC address, payment link) | +| `payout` | [`PaymentInstruction`](#PaymentInstruction) | Y | Object that describes how be paid by the PFI (e.g. BTC address, payment link) | + + +#### `PaymentInstruction` +| field | data type | required | description | +| ------------- | --------- | -------- | ------------------------------------------------------------------------- | +| `link` | String | N | Link to allow Alice to pay PFI, or be paid by the PFI | +| `instruction` | String | N | Instruction on how Alice can pay PFI, or how Alice can be paid by the PFI | + + +#### Example OrderInstructions +```json +{ + "metadata": { + "from": "did:ex:pfi", + "to": "did:key:z6MkoreC1URMQEVKm8wxzhVFntLzvPXYbtH8CEFNKUcSkLWT", + "exchangeId": "rfq_01ha83f661fs2avj6qgdhxpg28", + "kind": "orderinstructions", + "id": "orderinstructions_01ha83f663e3e88fshb06h6g78", + "createdAt": "2023-09-13T20:24:37.315Z", + "protocol": "1.0" + }, + "data": { + "payin": { + "link": "https://example-pfi.com/payin?currency=usd&amount=200.20" + }, + "payout": { + "instruction": "BTC will be paid to the provided BTC address" + } + }, + "signature": "eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa29yZUMxVVJNUUVWS204d3h6aFZGbnRMenZQWFlidEg4Q0VGTktVY1NrTFdUI3o2TWtvcmVDMVVSTVFFVkttOHd4emhWRm50THp2UFhZYnRIOENFRk5LVWNTa0xXVCJ9..R_BBKJoWifPFh10GJ1ij2gCCxND1CdzKbiOgPCIha__0GvRy0rHYCi18-TY7jNARaQ94RHXHYIsCRm2MuOPACw" +} +``` + + ### `OrderStatus` > PFI -> Alice: "Here's the status of your order."