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

Change Request: Currency conversion support in Mojaloop #89

Open
mjbrichards opened this issue Jun 15, 2021 · 75 comments
Open

Change Request: Currency conversion support in Mojaloop #89

mjbrichards opened this issue Jun 15, 2021 · 75 comments
Assignees
Labels
fspiop-change-request A change request for the FSPIOP API ml-ccb CCB issues or issues common to all the SIGs

Comments

@mjbrichards
Copy link

mjbrichards commented Jun 15, 2021

Open API for FSP Interoperability - Change Request

Table of Contents

1. Preface

___

This section contains basic information regarding the change request.

1.1 Change Request Information

Requested By Michael Richards, ModusBox
Change Request Status In review ☒ / Approved ☐ / Rejected ☐
Approved/Rejected Date

1.2 Document Version Information

Version Date Author Change Description
1.0 2021-06-15 Michael Richards Initial statement.

2. Problem Description

___

2.1 Background

There has been discussion for some time in the Mojaloop community about how to manage currency conversion as part of funds transfers. A version of this has been implemented by Mowali, but this has operational drawbacks which have prompted a search for a simpler and more robust alternative. Following discussions with interested parties, an initial statement of a proposed solution has been created and is attached to this document.

This solution (like any solution to the problem of currency conversion) will require some changes to the FSPIOP API, and these are described in Section 6.1 of the attached document. It is proposed that the required changes should be non-breaking, but this is a matter for review.

2.2 Current Behaviour

The API does not support currency conversion at present.

Explain how you would like the API to behave.

The attached document explains the proposed behaviour

3. Proposed Solution Options

___

API Candidate for currency conversion support in Mojaloop.docx

@elnyry-sam-k elnyry-sam-k changed the title Change Request: <Change Request Title> Change Request: Currency conversion support in Mojaloop Jun 16, 2021
@elnyry-sam-k elnyry-sam-k added the ml-ccb CCB issues or issues common to all the SIGs label Jun 16, 2021
@henrka
Copy link
Contributor

henrka commented Jun 21, 2021

Thanks @mjbrichards!

In general, it would be great to include sequence diagrams as discussed in CCB meeting. Please find some initial comments below before the sequence diagrams are shared:

  • Section 2, bullet 2: "We assume that the list of currencies in which a payee’s account(s) can receive is returned as part of the discovery process (i.e. by an existing PUT /parties call in the FSPIOP API.)" - There is no existing PUT /parties callback that returns a list of currencies. There have been different proposals for this, but it's not part of version 1.1. This should then also be part of Section 6.1.
  • Section 3.2: Is the currency purchase as described here is related to a specific transfer, you just buy some amount in another currency that can later be used for a number of different transfers, or is it meant for just one specific transfer?
  • Section 5.2.1: "It adds an expiry date, ..." in Section 6.3.5 it seems like a duration would be added. An expiry time seems more approriate than a duration, as you would in general be interested in when the conversion will expire, not the duration of it.
  • Section 6.1.4: "Changes to the /transfers response" Change response to callback
  • Section 6.2.1.1.2: There is a maximum of 10 FXPs now, suggesting to change to 8, 16 or 32 as we usually have powers of two (2^n) for sizes in at least FSPIOP API.
  • Section 6.2.2: "agreeFx" - Suggesting to rename this to fxQuote to align with FSPIOP, as its "used to ask an FXP to provide a quotation for a currency conversion".
  • Section 6.2.2, bullet 1: "conversionId" - Suggesting to rename this to fxQuoteId to align with above
  • Section 6.2.2, bullet 2: "source.account" - Suggesting to move configuration regarding ledger account type to Settlement API or similar. This should not be decided per transaction, rather it should be a generic configuration based on for example currency pair, or FXP, or a combination.
  • Section 6.2.2, bullet 4: "fxp" - Suggesting to rename this to fxpId
  • Section 6.2.2, bullet 5: "source.amount.currency" - There is no currency as part of data type Amount. To include a currency, data type Money should be used. But note that data type Money requires both an amount and a currency, so you would probably want a source.amount and a source.currency, where the amount is optional.
  • Section 6.2.2, bullet 8: "target.amount.currency" - See comment above regarding source
  • Section 6.2.3: "commitFx" - Suggesting to rename this to fxTransfer to align with FSPIOP.
  • Section 6.3.2: To keep this simple in the first version as it seems like for example Mowali would like to have this sooner rather than later, can we remove the charges from the data model, and let the FXP incorporate any charges/fees in the FX quote? Has it been required from anyone to have charges as a specific field? This should also allow the amountType to be removed, as there would not be any fees..
  • Section 6.3.2 and 6.3.3: Suggesting to rename FxpParticipant to FxParticipant, as it is a participant in the FX conversion, not in the FXP itself.
  • Section 6.3.4 (and 6.3.5): Why the use of duration instead of an expiry time? To know when the conversion will expire (which is what you generally would want to know), you now need to know the start of the duration and calculate the end time from there.

@mjbrichards
Copy link
Author

Thanks very much as usual, @ehenrka, for your detailed and penetrating comments. I attach a revised version of the proposal with changes tracked, and a commentary on your points below:

_- Section 2, bullet 2: "We assume that the list of currencies in which a payee’s account(s) can receive is returned as part of the discovery process (i.e. by an existing PUT /parties call in the FSPIOP API.)" - There is no existing PUT /parties callback that returns a list of currencies. There have been different proposals for this, but it's not part of version 1.1. This should then also be part of Section 6.1. _ Agreed. Added as Section 6.1.1
- Section 3.2: Is the currency purchase as described here is related to a specific transfer, you just buy some amount in another currency that can later be used for a number of different transfers, or is it meant for just one specific transfer? The plan is to add support for bulk currency purchases by DFSPs; but a detailed proposal for this is outside the scope of V1.
- Section 5.2.1: "It adds an expiry date, ..." in Section 6.3.5 it seems like a duration would be added. An expiry time seems more approriate than a duration, as you would in general be interested in when the conversion will expire, not the duration of it. We've been talking in the DA about using durations instead of times where we're talking about times which will be used to do things like time transfers out. This is based on the idea that, where a time is generated by one party (such as the payer DFSP) but enforced by another party (such as the switch) it makes more sense for the originating party to send a duration, which will allow the enforcing party to use its internal clock directly. However, this is not the case here (or in a PUT /quotes,) since the originating and evaluating parties are the same; so I thought a DateTime was appropriate. I'm not wedded to it, though...
- Section 6.1.4: "Changes to the /transfers response" Change response to callback Done (and for 6.1.2)
- Section 6.2.1.1.2: There is a maximum of 10 FXPs now, suggesting to change to 8, 16 or 32 as we usually have powers of two (2^n) for sizes in at least FSPIOP API. Done
- Section 6.2.2: "agreeFx" - Suggesting to rename this to fxQuote to align with FSPIOP, as its "used to ask an FXP to provide a quotation for a currency conversion". Done
- Section 6.2.2, bullet 1: "conversionId" - Suggesting to rename this to fxQuoteId to align with above Done
- Section 6.2.2, bullet 2: "source.account" - Suggesting to move configuration regarding ledger account type to Settlement API or similar. This should not be decided per transaction, rather it should be a generic configuration based on for example currency pair, or FXP, or a combination. I put this in to support bulk currency purchases, to allow the payer DFSP to specify that it wanted a conversion to be sourced from the bulk purchase (which would not, for instance, be liquidity checked by the switch...) But I'm happy to leave it out for now until we consider bulk purchases in more detail.
- Section 6.2.2, bullet 4: "fxp" - Suggesting to rename this to fxpId Done
- Section 6.2.2, bullet 5: "source.amount.currency" - There is no currency as part of data type Amount. To include a currency, data type Money should be used. But note that data type Money requires both an amount and a currency, so you would probably want a source.amount and a source.currency, where the amount is optional. Well spotted. Added to the document
- Section 6.2.2, bullet 8: "target.amount.currency" - See comment above regarding source
- Section 6.2.3: "commitFx" - Suggesting to rename this to fxTransfer to align with FSPIOP. Done
- Section 6.3.2: To keep this simple in the first version as it seems like for example Mowali would like to have this sooner rather than later, can we remove the charges from the data model, and let the FXP incorporate any charges/fees in the FX quote? Has it been required from anyone to have charges as a specific field? This should also allow the amountType to be removed, as there would not be any fees.. Henrik, Henrik: I only put these in because of the long discussion we had about how we might need them. I'm leaving them for now...
- Section 6.3.2 and 6.3.3: Suggesting to rename FxpParticipant to FxParticipant, as it is a participant in the FX conversion, not in the FXP itself. Done
- Section 6.3.4 (and 6.3.5): Why the use of duration instead of an expiry time? To know when the conversion will expire (which is what you generally would want to know), you now need to know the start of the duration and calculate the end time from there. See my discussion above, but you're right: I let my enthusiasm get the better of me. Modified

The revised document:
API Candidate for currency conversion support in Mojaloop v1.1.docx

@mjbrichards
Copy link
Author

Sequence diagram to illustrate the transfer flow:
Transferring with Currency Conversion(DFSP-based)

@henrka
Copy link
Contributor

henrka commented Jun 29, 2021

Thanks @mjbrichards, just a quick follow-up comment regarding my own comment. It should be fxQuotes instead of just fxQuote, and similarly fxTransfers instead of just fxTransfer (missing s in the end) to align with FSPIOP API. Sorry for that.

One more comment for now regarding the sequence diagram, there is a comment between step 29 and 30 which talks about fees charged in target currency and how they should be converted. In the data model for Conversion, there is only a currency and amount, the FXP don't know about the actual transfer and any fees as part of it. Have I missed something, or is the sequence diagram not correct for this part? The sequence diagram way seems more complicated, as the FXP would need to know more about the FSPIOP API than what should be necessary..

@mjbrichards
Copy link
Author

Thanks for your comments, @ehenrka, and my apologies for not responding to them sooner. I should have said "charges" rather than "fees". The FXP will, I think, need to ensure that it quotes its charges in the source currency as well as in the target currency, so that the payee DFSP can include them in the fees it quotes to the payer DFSP if required. I have also completed a candidate version of the API for review:

API Candidate for currency conversion support in Mojaloop v1.2.docx

@henrka
Copy link
Contributor

henrka commented Aug 23, 2021

Thank you @mjbrichards, please find a new round of comments on the latest version:

  • Section 1.1, "Custodian" could be removed as a term as it is never actually used in the document as far as I can see, expect in the definition of FXP. FSP can be used there instead as this term should be known by the existing FSPIOP API.
  • Section 5.2, Reference to Section 6.3.1 is not correct, should probably be 6.2.2. There also seems to be other references in the document that are not really correct..
  • Section 5.2.1, "It adds a duration, ...", as my earlier comment I would like to have an expiry time instead of a duration (the original problem was the inconsistency between sections), as it's much easier to know when the conversion is expired. A duration would just move the problem into calculating when the message was sent from the other party to find the actual expiry. Section 6.2.5 uses expiration.
  • Section 5.3, Step 2.c.i, "commitFx" should be "POST /fxTransfers"
  • Section 6.1.1, There is a maximum of 10 currencies now, suggesting to change to 8 or 16 as we usually have powers of two (2^n) for sizes in at least FSPIOP API.
  • Section 6.1.3.1, the description for sendAmount is not correct. This is also a breaking change from the existing amount in the Transaction object.
  • Section 6.2.2, I'm still not convinced on the need of having specific fees/charges for FXP instead of just having any charges as part of the FX rate.. It's strange to me to have fees in two entirely different elements; in the payeeFspFee in PUT /quotes and then in the new SignedConversion object. See also Assumption nr 8 (in Section 2 in the document). This inconsistency will probably lead to implementation errors.
  • Section 6.2.6, "Duration" is never used now, expect in text in Section 5.2.1 (see other comment above).
  • Section 6.3.2, Step 7, both alternatives seems to be that the amount in target currency should be calculated. One of them should probably be amount in source currency?
  • Section 6.4 can be removed (is empty).

May I please ask you to add some more detailed example sequence diagrams, where it is possible to see the source and target amounts, and the different fees/charges and who is paying them (if we really want to have charges from the FXP, which I preferably would like to avoid for now)? It would help a lot to have more specific examples to validate the proposed changes.

One thing to consider is that it will not be possible for a FSP to completely subsidize a transaction that is using FX (see for example Section 5.1.2.1 in FSPIOP API), as the FXP can add additional fees after the Payee FSP has determined its final terms of the transfer. The FXP fees would at least be invisible if there is only a FX rate which includes fees.

@millerabel
Copy link
Member

millerabel commented Aug 23, 2021 via email

@mjbrichards
Copy link
Author

My thinking on this point was as follows.

If the FXP can only express charges by incorporating them into the conversion rate, then the charges will inevitably be passed on to the debtor customer. I did not want to rule out the possibility that the creditor FSP might decide to bear some or all of the conversion charges itself.

So the FXP can return one or more charges as part of its response to the creditor FSP. It will add these either to the amount to be sent or the amount to be received, depending on the setting of the amountType field in the base transaction. Now, the creditor DFSP can decide either to leave the amounts as they stand, or can remove charges from the total amounts to be transferred if it proposes to bear some or all of those charges itself. If the amounts are left as they stand, then of course the creditor need do no more than accept the quotation as it is returned by the FXP, and adjust the send and receive amounts of the quotation appropriately.

All of this activity has taken place prior to the creditor DFSP's signature of the terms of the transfer (which will include the information returned by the FXP.) So the final terms of the transfer have not yet been determined, and hence it is still legitimate for the creditor DFSP to make any changes it wants to before finally signing the terms and adding an expiry to them.

It seemed to me that this structure would allow individual schemes to be flexible in their approach, while not adding intolerably to the complexity of individual implementations. If the FXP does not want to include charges, or if the creditor FSP simply wants to pass charges on to the debtor FSP, then nothing more need be done (except, in the latter case, accepting the amounts returned by the FXP; but that would be good practice in any case.) I am sensitive to your point about avoiding complexities which might cause problems in implementation; but I hope I've done enough to clear myself of that charge. I will include some of this thinking in the detailed sequence diagram I'm working on...

@henrka
Copy link
Contributor

henrka commented Aug 24, 2021

Hi @millerabel, my point was just regarding the possibility for a Payee FSP to subsidize fees for a Payer. For example in a cash-in where the Payer FSP would like to receive a fee to be able to pay out the commission to its agent, the Payee FSP could subsidize the fees so that the cash-in is free for the consumer. An example of this can be seen in https://docs.mojaloop.io/mojaloop-specification/fspiop-api/documents/API%20Definition%20v1.1.html#5162-agent-initiated-cash-in-send-amount. If it was not possible for the Payee FSP to subsidize the FXP fees, then this would not be possible.

But after @mjbrichards further comment regarding this, I might have interpreted the text in Step 25 ("Here is the finalised version of the transfer") in the sequence diagram (#89 (comment)) too strict, as it seems like the transfer is not completely finalized in this stage.

@mjbrichards
Copy link
Author

Attached is a detailed sequence diagram of a transfer including currency conversion. Let me know if you have any comments.
Transferring with Currency Conversion(DFSP-based)

@henrka
Copy link
Contributor

henrka commented Sep 2, 2021

Thanks @mjbrichards, great to have a more complete example to review where it is easier to understand your thoughts.

Please find some comments below:

  • Step 8: Why is it called denomination instead of currencies, as it seems to be the currency that is returned and not the denomination? (see for example https://en.wikipedia.org/wiki/Denomination_(currency)).
  • Step 13-19: Why does the Payer FSP need to know about which FXPs there are, as it will be the Payee FSP who asks for the conversion from the FXP? Is it a requirement on Payee FSP to use that specific FXP, or can it use other FXPs? What happens if the Payee FSP can't access the FXP in the conversion object? Maybe just include information that a currency conversion is needed, then it's up to the Payee FSP in that case to select one?
  • Step 20: There is a lot of repeated information in the request now to include the conversion object. There does not seem to be anything specific in the conversion object that is really needed by the Payee FSP that is not available in the existing data model for POST /quotes, at least not for this example. It should know the information from the existing POST /quotes request, unless there is a requirement that the Payer FSP decides the FXP (see above). The Payee FSP knows the currency of the Payee. In this example the Payer would like to send 100 USD from their account, which the Payee FSP then knows that this needs to be converted into a currency that the Payee can receive in. Any fees are deducted from the amount that the Payee will receive as it's a SEND amount.
  • Step 25: The Payee FSP could have created the conversion object (see above).
  • Step 29-30: The target->amount->currency should be RWF in the agreedConversion object?
  • Step 29-30: How will other participants know who should receive a charge in the agreedConversion object? Why does the Payer FSP need the agreedConversion object, is it just for knowing the fee, or because it is needed by the Switch in steps 44-45?
  • Step 35: Just for my confirmation, the payeeFspFee should contain both the Payee FSP fee and the FXP fee, which means the Payee FSP is required to handle this? It simplifies a bit for the Payer FSP as it does not need to actually check the signedConversion object, but makes the name of field a bit strange as it is not only the Payee FSP fee. Ideally, the Payer FSP should not even have to care about the signedConversion, a fxpFee could be added instead to know about the FXP fees. Additionally, an optional FX rate could be good to send as well in the PUT /quotes.
  • Step 41-46: Is it really necessary to include the entire signedConversion object again in the POST /transfers? For example for the quote, we just have the ID of the quote instead of resending the entire object again, so the API is not really consistent here. To me, it seems easier if the Switch would just reserve the funds for the Payer FSP in Step 43-45, and then reserve the FX part between Step 49 and 50 when it gets the POST /fxTransfers. This would also align better with a currency pre-purchase option, as the reservation should then happen in the POST /fxTransfers request to the Switch.
  • Step 45: How will the Switch know which fee should go to which participant?

To me, based on the comments above, it seems like there are some simplifications that could possibly be done which should make the API easier and more consistent:

  • Possibly avoiding the conversion object in POST /quotes, or at least simplify it.
  • Avoid sending the signedConversion object in PUT /quotes and POST /transfers. If this is required just for sending the FXP fee to the Payer FSP and for knowing in Switch what should be reserved, I would rather have the FXP fee in a specific object and the reservation to be performed as part of POST /fxTransfers (which would be aligned with a currency pre-purchase). I don't see a need for the Payer FSP to validate the exact terms with the FXP, as long as the Payee is paid by the Payee FSP according to the terms in quote.

@jmssebunnya
Copy link

jmssebunnya commented Sep 5, 2021

@MichaelJBRichards will be happy to chat about this in our next session to further align/understand how the proposal is better or improve on insistence such as the current Mowali implementation.

@lewisdaly
Copy link
Contributor

Step 20: There is a lot of repeated information in the request now to include the conversion object.
I agree @ehenrka - it seems to me that if a Payee DFSP is going to do the conversion, then the Payee DFSP should imply this based on the quote#amount.currency field.

I think the Payee DFSP should be free to choose whichever FXP they want, and also create the conversionId themselves, since it is the Payee who is asking for the conversion to take place.

@mjbrichards
Copy link
Author

Apologies for the delay, folks, but I have now completed a general revision of the structure of the FX API, based on @ehenrka's comments. I have versioned it as 2.0. I will produce two new sequence diagrams showing how the process will work depending on whether it is the payer DFSP or the payee DFSP who undertakes the currency conversion. Hopefully you will have enough time to take a brief look before this afternoon.

API Candidate for currency conversion support in Mojaloop v2.0.docx

@millerabel
Copy link
Member

millerabel commented Sep 21, 2021 via email

@mjbrichards
Copy link
Author

In this model, the sender can select the FXP (and manage the conversion) if they want to. If they don't, they can resign the task to the payee. If the payee doesn't either, then the transfer is rejected. Similarly, the FXP can decline to execute a requested conversion...

elnyry-sam-k added a commit to elnyry-sam-k/mojaloop-specification that referenced this issue Sep 30, 2021
@mjbrichards
Copy link
Author

Please find attached a revised API definition, together with sequence diagrams showing how the new API will work when the payer DFSP does the currency conversion, and when the payee DFSP does the currency conversion.
API Candidate for currency conversion support in Mojaloop v2.0.docx
![Transferring with Currency ConversionPayee DFSP requests conversion](https://user-images.githubusercontent.com/35496554/136356953-2f4925cf-915b-4743-b
Transferring with Currency ConversionPayer DFSP requests conversion
186-0ab9e085e5bd.png)

@millerabel
Copy link
Member

millerabel commented Oct 7, 2021 via email

@lewisdaly
Copy link
Contributor

Thanks @MichaelJBRichards for the updated doc and sequence diagram. I'm reading through the doc now.

The case where the Payer DFSP specifies a SEND amount is clear to me, since the Payer DFSP can know the total amount before sending the POST /quotes to the Payee, but I'm still unclear on how the RECEIVE amount works.

From your doc:

• If the amount is a RECEIVE amount, then the payer DFSP will have been informed of the amount to be received before it makes the request, and will use that amount in the quotation request.

The Payer DFSP knows the "amount to be received", but this doesn't include any fees added by the Payee DFSP, so the Payer DFSP can't know (until after receiving the PUT /quotes/{ID} back from the Payee DFSP) what to put in the POST /fxQuotes API call.

@mjbrichards
Copy link
Author

There goes cut'n'paste again, @millerabel, showing me up for the lazy drone I am... I attach a revised version.

The way I think this would work, @lewisdaly, is as follows. If the payee DFSP intends to add fees, then it will do so in the normal way and return the quote to the payer DFSP. The payer DFSP now knows the total amount that must be received by the payee DFSP in the target currency, so it can ask the FXP: "what amount of the source currency do I need to send you to get that total in the target currency?" This requires, of course, a further call to the FXP to obtain the new amount in the source currency. Does that answer your question?
Transferring with Currency ConversionPayer DFSP requests conversion

@henrka
Copy link
Contributor

henrka commented Oct 11, 2021

Thanks @mjbrichards for the updated document and sequence diagram! Please find some initial comments from me below.

First some quick comments/questions from the document:

  • Section 4.3: Please clarify that the quotation request discussed in the bullet points is the FX quote, and not the FSPIOP API quote
  • Section 4.4: What is the "validation request"? This is just mentioned in this section. Is it the POST /fxQuotes?
  • Section 5.3: The execution request, is this the POST /fxTransfers?
  • Section 6.1.1: "receiveCurrency" should be "receiveCurrencies"
  • Section 6.2: "New data objects in the FSPIOP API" This should probably be FXP API now?
  • Section 6.2.2: "One or more charges which the FXP intends to levy as part of the currency conversion, or which the payee DFSP intends to add to the amount transferred." How will we know which participant that wants which fee, as the charge does not say which one that would like to have the fee? Could we simplify this into two elements instead, one for the FXP and one for the Payee FSP? I don't really see why the FXP would want more than one single fee, as they could just handle any separation of fees internally as part of their logic, as such it seems a bit unnecessary to have more than one fee in the API. For the Payee FSP there is never more than one fee.
  • Section 6.2.5: Can we please include the FX rate in some element, as this is important to show to the customer?
  • Section 6.2.6: Duration is no longer used as far as I can see?
  • Section 6.3.1.1.2: Text says 10 participants, data model says up to 16. Additionally, could we simplify the services service a bit and for every type of service use the same callback? I.e. "providers" instead of "fxpProviders", "The FSP Id(s) of the participant(s) who offer the requested services." instead of "The FSP Id(s) of the participant(s) who offer currency conversion services.". You know the providers are FXPs as the the callback is PUT /services/FXP. The same data model could then be reused between services.
  • Section 6.3.2.1.2: Are both IDs in POST /fxQuotes and in Conversion needed? Seems a bit unnecessary to have two..
  • Section 6.3.3: Would it not make sense to have the POST /fxTransfer to be more similar to POST /transfers? Currently the signedConversion is and conversion is passed around a lot.

Some questions/comments from the sequence diagram:

  • Step 8/9: "denomination" should be "receiveCurrency" according to the document (which should be "receiveCurrencies" according to comment above)
  • Step 38: "It'll cost 175 XOF" , can we add a " ... in fees" to that to increase clarity?
  • Step 44: Would be great to detail the exact reservations as part of this step, including Payer FSP, Payee FSP, and FXP.
  • Step 48/49: The state here is "COMPLETED", but I guess it should just be "RESERVED"? And then completed in step 68 using PATCH /fxTransfer?
  • Step 51: Include the actual error callback
  • Step 55: The amount and/or currency must be incorrect. Amanda would just like to send 5000 XOF. The amount is now 9850 XOF. The 150 (10000-150=9850) is the FXP charge in RWF, but this must also be sent from the Payer FSP, or is this charge already handled as part of the /fxTransfers flow? I would assume that the Payer FSP requests a transfer of 5000 XOF, which is then converted to RFW in the Switch using the earlier agreed conversion with this relatedTransferId?
  • Step 57: Would be great to include the actual reservations here (analogous to comment in Step 44).
  • Step 63: The "output message from the queue" seems very specific to the actual implementation in Mojaloop, and does not have much to do with the currency conversion? Suggest to remove in that case.
  • Step 65: Should this not say "commit" instead of "cancel"?
  • Step 67: I guess this should be PATCH /fxTransfer?

Generic questions/comments:

  • Would be great to have a sequence flow example of RECEIVE amount as well.
  • In the current Mowali setup, each country (or currency) is divided in different monetary zones. This simplifies the scheme setup a bit, as every FSP does not need to know about every FSP in other monetary zones. An FSP only needs to know about FSPs and their certificates for signatures in the same monetary zone. Don't know know if something similar will be supported here, or if every FSPs needs to know about anyone? I haven't given this much thought yet, it was just something that popped up in my mind now..

@henrka
Copy link
Contributor

henrka commented Jun 13, 2023

Thanks @MichaelJBRichards,

Comments including some follow up on earlier discussions, sorry if I'm repeating myself:

  • Why is dependents still part of the POST /quotes? The Payee FSP shouldn't care about the FX as the amount is already in the Payee FSP's currency, if the Payer FSP has already decided to use the FXP's service of performing the FX. As stated in earlier comments, the Payee FSP can't decide to offer any new terms in the quote callback, it can just either agree with an optional payeeFspFee, or reject the quote.
  • Why is the dependents part of POST /transfers? The FX transfer is already reserved in the POST /fxTransfers part (Step 42 to Step 53), it should just need to be committed. The condition and fulfillment for the FX transfer part should be enough for that?
  • Step 60 says "Let me check that the terms of the dependent transfer are the same as the ones I agreed to and that the fulfilment and condition match". Why would the Payee FSP care about the dependent transfer as long as it gets the correct amount? Payee FSP should just be interested in that it gets its 9850 RWF so that it can send the promised 9650 RWF to the Payee. How the Payer FSP converts XOF to RWF shouldn't be important for the Payee FSP.

By removing these two, you would remove potential business sensitive information containing the FX rate from the Payer FSP to the Payee FSP, while still allowing the message to be signed.

@MichaelJBRichards
Copy link

But surely both parties will always know the exchange rate, because the quote response contains both the send amount and the payee receive amount? And the sending customer needs to know both.

I still think that the currency conversion, whichever party executed it (and both might), needs to form part of the terms on which the payment was agreed; not least because one of our RESTful principles is that a message should contain all of the information which the recipient needs to act on it. This means, I think that the PUT /transfers message needs to contain all of the information that the switch will need to assign the values correctly to its ledgers. You will note that I have restricted the definition of a dependent to include only the participant and the amount; but these are, I think, necessary for the switch to be able correctly to distribute the obligations incurred as a consequence of the payment. So the payee institution will definitely need to know which FXP performed the conversion; and, since this is the case, I saw no good reason not to include the information in the terms of the payment.

@henrka
Copy link
Contributor

henrka commented Jun 15, 2023

But surely both parties will always know the exchange rate, because the quote response contains both the send amount and the payee receive amount? And the sending customer needs to know both.

No, the quote callback only contains the amounts in the Payee's currency (see note between Step 34 and 35 in your flow, where all amounts are in RWF). The Payer FSP already knows the currency conversion required from the POST /fxQuotes and its callback (see Steps 20 to 28), and can as such show the amounts in both currencies to the Payer in Step 39. The Payer FSP will then use POST /fxTransfers to perform the required conversion (see note between step 42 and 43). This way there is no need for the Payee FSP to know anything about if or how a currency conversion has been performed, perhaps the Payer FSP had an account in both currencies that could be used for the transfer.

I still think that the currency conversion, whichever party executed it (and both might), needs to form part of the terms on which the payment was agreed; not least because one of our RESTful principles is that a message should contain all of the information which the recipient needs to act on it.

We are not sending all information in all requests today. For example in the POST /transfers, we are relying on information that has earlier been agreed in the POST /quotes. We are not stating that we are fully RESTful compliant. Why is it necessary for the Payee FSP in the flow above to know that a currency conversion has been performed, as long as it receives the required funds in the correct currency in some way from the Payer FSP? Maybe there is some very important reason, but then I would like to understand it.

You will note that I have restricted the definition of a dependent to include only the participant and the amount; but these are, I think, necessary for the switch to be able correctly to distribute the obligations incurred as a consequence of the payment.

The Switch has already reserved the transfers related to the currency conversion in earlier steps (POST /fxTransfers and its related callback, Steps 42 to 53), now it just needs to commit what has earlier been reserved? Sending the same thing again in POST /transfers as in POST /fxTransfers would just introduce potential errors in my view. Minimizing the information in POST /transfers would also minimize the changes needed for existing implementers.

So the payee institution will definitely need to know which FXP performed the conversion; and, since this is the case, I saw no good reason not to include the information in the terms of the payment.

Is this necessary per transfer in the Payee FSP? What I as a Payee FSP is interested in per transfer is that I have received the funds from the Payer FSP in my account at the Switch in some way so that I can be assured that I can perform the transfer to my account holder. As a Payee FSP I don't really care about the internal accounting in the Switch, i.e. if there were some other participant in between, such as the FXP, that actually sent the funds to me. In the Switch it is vital information to keep track of the internal accounting. But in the integration between the Payer FSP and Payee FSP, I don't see it as required information. But maybe there are other principles in Mojaloop that requires this?

@PaulGregoryBaker
Copy link

I like the fx flow above.
Doing the POST \fxQuote before the POST \transfers only works for the transfer amount type 'RECEIVE'.
To support receive transfer type presumably the POST \quotes would have to be called before the POST \fxQuote.
Maybe we should build another sequence diagram for that.

@MichaelJBRichards
Copy link

Thanks for your comments, @PaulGregoryBaker. I'm afraid we shan't be able to call quotes before we call fxQuotes, since the condition returned by fxQuotes forms part of the terms of the payment. Since this is the case, generation of a new condition in the process of re-quoting for currency conversion would invalidate the condition in the quote response for the transfer. In the case where the amountType was RECEIVE and the creditor party added fees, the debtor party would have to go round another quotation and approval cycle including the new conversion.

I've done some further thinking around the points raised by @henrka. Essentially, I think that reducing the information in the message to preserve the privacy of the conversion, as @henrka rightly proposes, implies further effort on the part of the switch. In previous incarnations, the burden on the switch (and especially the code changes required) were relatively light. I think that they are now more extensive, so I've included a separate document where I set out what I think they might be.

I've also revised the sequence diagram, and have aligned it with the latest state of the API document - in our various modifications it had got somewhat our of kilter. I attach all three documents, and await your comments with interest.

Converting dependencies to obligations.docx
API Candidate for currency conversion support in Mojaloop v2.4.docx
FXNext Version 2 4 - Payer conversion

@PaulGregoryBaker
Copy link

Nice. There is a small error in the text description on 39.

@MichaelJBRichards
Copy link

FXNext Version 2 4 - Payee conversion

A version of the sequence diagram showing currency conversion being initiated by the creditor participant.

@henrka
Copy link
Contributor

henrka commented Jul 5, 2023

Thanks @MichaelJBRichards,

Please find my initial comments/questions on the new flow where the Payee FSP performs the currency conversion:

  • Minor layout issue, "Amanda" should be named "Mulenga Kapwepwe" instead according to the flow.
  • PUT /fxQuotes/ in note between Steps 29 and 30 should contain a validity field according to the Word document.
  • ilpPacket in note between Steps 35 and 36, shouldn't that be named transaction instead, as in note between Steps 42 and 43 for POST /transfers?
  • transferAmount in note between Steps 35 and 36 should use currency MWK, not MWS.
  • I don't know if I have already asked this before, but why are dependent transfers identified by their condition instead of their correlation ID? I guess there is a good reason for this?
  • Minor layout detail, the Payee FSP named Zoona seems to incorrectly activated around Step 37.
  • The payeeFspFee should preferably be in Payer FSP's currency (MWK), as the Payer FSP in this case wouldn't like to deal with the Payee's currency (ZMW). In Step 39-40 the fee of 5 ZMW is magically converted to 250 MWK, without the Payer FSP knowing the FX rate.
  • The transaction object (that is called ilpPacket in POST /quotes, see earlier comment) differs between PUT /quotes and POST /transfers, this should be the same.
  • In Steps 44-45, shouldn't there be some logic in the Mojaloop Switch to handle the Payee FSP's currency, stating that the Payee FSP doesn't have an account in MWK and that the dependent transfer needs to be executed by the Payee FSP for the transfer to be reserved?
  • In note between Steps 49 and 50, the targetAmount is different from the targetAmount in PUT /fxQuotes/, which is a bit confusing logic (3 ZMW in charges has been withdrawn). To me, the logical thing would be to have the same targetAmount, but also including the same charges as before.
  • In note between 53 and 54, shouldn't there be a reservation for Zoona in ZMW in the Switch as well, as they want to perform the currency conversion? The reservation for Zoona doesn't seem to be showed in the flow anywhere..
  • If the conversion failed in Steps 59-60, the FXP should also be informed of the failure using a PATCH notification (PATCH /fxTransfers/...)?

@MichaelJBRichards
Copy link

Thank you, @henrka , for your usual careful review. I have been doing some refactoring in order to align the FX API more closely with the way in which I would expect the ISO 20022 version of the FX API to work, and I will describe that later; but I will start, as usual, by commenting on your comments:

  • Amanda, I am happy to report, has left the building
  • Typos in points, 2, 3, 4, 6, 8 fixed
  • Point 5 superseded by refactoring - see below
  • Point 7: you're right, fixed
  • Point 9: added functionality in switch
  • Point 10: have changed this round to allow correct calculation of the exchange rate.
  • Point 11: there is no reservation against Zoona because a) they don't have an MWK account and b) their request for currency conversion is funded by the funds to be received from NBS, so they are not net debtors in the transfer.
  • Yes, this would ordinarily be the case; except that the failure at 59-60 is initiated by the FXP...

When I started thinking about how we might represent currency conversions in ISO 20022, it seemed to me that we wanted to make the content of the currency conversion endpoints as close as we could to the ordinary payment endpoints. So a currency conversion execution would be strongly analogous to an ordinary payment execution. The main obstacle to this seemed to me to be the dependents structure. I therefore propose that we should replace the dependents structure, in which the payment contains all the information about the subordinates (such as the conversion), with an additional field in the subordinate which links it to the master payment, using the master payment's transaction ID. This would, I think, achieve the same effect but with greater simplicity for the data structures (and it removes your reservation about using the condition...) I have produced a new version of the draft API specification incorporating this idea, and attach it, together with a new sequence diagram which shows the flow for a payment where conversion is done by the payee, but the amount is a RECEIVE amount (i.e. something like a merchant payment.) Let me know what you think.

API Candidate for currency conversion support in Mojaloop v2.6.docx
FXNext Version 2 6 - Payee conversion with RECEIVE amount

@henrka
Copy link
Contributor

henrka commented Jul 20, 2023

Thanks @MichaelJBRichards,

The changed flow without dependents is better! Please find my comments as usual (now with numbers instead to make it easier to relate to which one it is):

  1. Step 8: As per our recent discussion in Slack to allow the Payee FSP to know the Payer's currency in case the Payee FSP should perform the currency conversion, it would make sense to change receiveCurrencies to supportedCurrencies, or just currencies instead. The currency information of the Payer would then be sent as part of the Party information in the POST /quotes in Step 13.
  2. Step 13: See above to include Upile's currency.
  3. Step 17: The currency that Upile is using would need to be known at this step (see comments above)
  4. Note between 25 and 26: The amountType is SEND here, I would believe that is a copy-paste error and it should be RECEIVE?
  5. Note between 25 and 26: Could we remove the conversion type, and have its parameters on the root level of POST /fxQuotes instead? The conversion type doesn't seem to be reused, so I don't know if there is a need to have a specific type for it? This would make the PUT /quotes more similar to data models for other requests in the FSPIOP API.
  6. Note between 25 and 26: Is there a need for both the conversionRequestId and conversionId ? Neither seems to be reused outside of the scope of the POST/PUT /fxQuotes.
  7. Note between 29 and 30: I suspect you have read too many ISO specs now when you are abbreviating transaction in governingTx :). POST /fxTransfers seems to use relatedTransactionId, can we please change to that or governingTransaction or similar? Also related to governingTx, how does FDH FX know the transaction ID, it is not sent in the POST /fxQuotes?
  8. Note between 29 and 30: Could we remove the conversionTerms type, and have its parameters on the root level of PUT /fxQuotes instead? The conversionTerms type doesn't seem to be reused, so I don't know if there is a need to have a specific type for it? This would make the PUT /fxQuotes more similar to data models for other requests in the FSPIOP API.
  9. Step 34, The text "OK, so I will charge 2 ZMW for this" would be easier to follow if it said something like "OK, I will add 2 ZMW on top of the conversion fee".
  10. Note between 35 and 36: The Transaction type (at least in current version) doesn't contain a payeeReceiveAmount, is this something that should be added or is the payeeReceiveAmount in the PUT /quotes enough?
  11. Step 39: This should be 250 MWK instead of 5 ZMW.
  12. Step 40: Mulenga will receive 100 ZMW according to the payeeReceiveAmount.
  13. Note between 42 and 43: See earlier comment on payeeReceiveAmount in Transaction type.
  14. Note between 49 and 50: relatedTransactionId, is this same as the earlier governingTx? Can we stick to one?
  15. Note between 49 and 50: The key to perform the FX Quote, wouldn't it be more logical for the FXP to use the ID of the FX Quote instead of the ID of the transaction (relatedTransactionId)? For the Switch the transaction ID is probably more important, but for the FXP the FX Quote should be the important key, so for me it would be logical to include both.
  16. Step 52: "Does the sender have an account in this currency?" Which one of the currencies? :)

@MichaelJBRichards
Copy link

New version of the FX functionality, which now supports conversion via a reference currency. The basic thinking behind this is:

  • The debtor DFSP asks the creditor DFSP to perform currency conversion between the source and target currencies in the normal way.
  • The creditor DFSP asks an FXP (FXPA) to provide currency conversion between the source and target currencies.
  • The FXP knows that it can provide conversion between a reference currency and the target currency. It modifies the proposed conversion to propose conversion between a reference currency and the target currency.
  • The creditor DFSP accepts this, and modifies the proposed transfer to use the reference currency. It signs the modified transfer terms.
  • The debtor DFSP recognises that it can't provide funds in the reference currency. It asks an FXP (FXPB) to provide conversion between the source currency and the reference currency. Note that this does not require any revision to the terms of the transfer.
  • The debtor DFSP requests execution of the initial currency conversion from the source currency to the reference currency. It gives the transfer ID as the transfer on whose success it depends. Funds are reserved in the source currency against the debtor DFSP.
  • The debtor DFSP requests execution of the transfer. Funds are reserved against FXPB in the reference currency.
  • The creditor DFSP requests execution of the conversion from the reference currency to the target currency. It gives the transfer ID as the transfer on whose success it depends. Funds are reserved in the reference currency against FXPA.
  • The creditor DFSP confirms the success of the transfer. Obligations are recorded for all parties by the switch:
    • Debit the debtor DFSP and credit FXPB in the source currency
    • Debit FXPB and credit FXPA in the reference currency
    • Debit FXPA and credit the creditor DFSP in the target currency

I attach the revised draft API and a sequence diagram illustrating this. Any comments you might have would be most welcome.
API Candidate for currency conversion support in Mojaloop v2.7.docx
FXNext Version 2 6 - Conversion via a reference currency with RECEIVE amount

@MichaelJBRichards
Copy link

My comments on your comments on the previous version of the sequence diagram:

  • 1, 2, 3, 4: fixed.
  • 5 and 8: I kind of like the idea of a conversion object, which I think of as containing the terms of the conversion in the same way as the transaction object contains the terms of the transfer. But I wouldn't die for it...
  • 6, 7, 9: fixed.
  • 10, 13: You are correct; I propose adding it to the transaction object in Section 7.1.3 of the API candidate.
  • 11, 12: fixed
  • 14: all instances now changed to determiningTransactionId
  • 15: I had thought to use the condition as the key for the FXP to recognise the terms it was being asked to execute: please execute the conversion for which these are the signed terms. But I'm open to persuasion.
  • 16: fixed

I also made a couple of adjustments so that the creditor DFSP asks for its fee to be converted. This means that in the execution of the conversion the sums come out right...

In a belated attempt to make this issue easier for all of us, I have uploaded current versions of the documentation to our own repo in GitHub.

@mdebarros
Copy link
Member

mdebarros commented Aug 17, 2023

I attach the revised draft API and a sequence diagram illustrating this. Any comments you might have would be most welcome. API Candidate for currency conversion support in Mojaloop v2.7.docx FXNext Version 2 6 - Conversion via a reference currency with RECEIVE amount

Just some review comments:

  1. step 13, the POST /quotes has a typo suppoertedCurrencies -> suppoertedCurroencies
  2. step 62, this should go to the Payer FSP
  3. step 83, this should go to the Payer FSP
  4. step 84, this should go from Payer FSP back to the switch

@MichaelJBRichards FYI

@henrka
Copy link
Contributor

henrka commented Aug 23, 2023

My comments on your comments on the previous version of the sequence diagram:

  • 1, 2, 3, 4: fixed.
  • 5 and 8: I kind of like the idea of a conversion object, which I think of as containing the terms of the conversion in the same way as the transaction object contains the terms of the transfer. But I wouldn't die for it...
  • 6, 7, 9: fixed.
  • 10, 13: You are correct; I propose adding it to the transaction object in Section 7.1.3 of the API candidate.
  • 11, 12: fixed
  • 14: all instances now changed to determiningTransactionId
  • 15: I had thought to use the condition as the key for the FXP to recognise the terms it was being asked to execute: please execute the conversion for which these are the signed terms. But I'm open to persuasion.
  • 16: fixed

I also made a couple of adjustments so that the creditor DFSP asks for its fee to be converted. This means that in the execution of the conversion the sums come out right...

In a belated attempt to make this issue easier for all of us, I have uploaded current versions of the documentation to our own repo in GitHub.

Thanks @MichaelJBRichards,

Follow up on comments

  • 5 and 8: Even if it is on the root level, you could still refer to it as the conversion object, for example "The FX conversion as specified in the POST /fxQuotes".. The transaction is a separate type because it is reused in different messages. I would at least vote for having it on the root level to align with other similar APIs in FSPIOP API :)
  • 10 and 13: The payeeReceiveAmount will then be duplicated and sent both in root level of PUT /quotes and in the transaction object. If we are moving away from ILP1 and the ASN1 encoded ILP packet, then we could probably remove it from the root level to avoid duplication if it is required in the transaction object instead, as this should be easily read directly in the JSON instead of in ASN1 encoded ILP packet.. Let's discuss.
  • 15: I guess it should be consistent with POST /transfers to use condition, so I should be fine with how it is proposed.

Comments on the new flow in https://github.com/mojaloop/Currency-conversion/blob/main/FXNext%20Version%202.7%20-%20Payee%20conversion%20with%20RECEIVE%20amount.svg

  1. Note between step 46 and 47: How does the Switch here know that FDH_FX will be involved? Shouldn't it be NBS Bank (the Payer FSP) that the funds are reserved for? This seems to be corrected in note between 55 and 56, so probably just a typo..
  2. Step 62 and 65 both goes to Payee FSP. Shouldn't step 62 be a PATCH /fxTransfer with transferState=ABORTED to the FXP, to inform that the FX transfer was aborted due to the fulfillment mismatch?
  3. Step 72: NBS should probably be deactivated in the PlantUML code here...
  4. Step 76: Should we add Mulenga Kapwepwe as an actor that is actually receiving the 100 ZMW here, just for clarity?

Comments on the new flow in https://github.com/mojaloop/Currency-conversion/blob/main/FXNext%20Version%202.7%20-%20Payer%20conversion.svg

  1. Note between step 29 and 30: Following up on earlier discussions, I don't really understand why the Payer FSP should share their fees (the "charges") to do the FX conversion to the Payee FSP, this should be none of the Payee FSP business. The charges doesn't seem to be used by either the Switch or the Payee FSP. Shouldn't this instead contain a "converter": "PAYER" to inform that the Payer FSP has performed a currency conversion? This information is part of the PUT /quotes, so I guess this is just missed to be updated?
  2. Step 38: Currency should be MWK, not MWF.
  3. Step 52: If fulfillment doesn't match then the FXP should probably be informed of the same via a PATCH /fxTransfer with transferState=ABORTED.
  4. Note between 57 and 58: Should there be information in the POST /transfers that there is a dependent FX transfer, or will the Switch check in its database for each transfer if there is a dependent transfer?
  5. Step 65: Should we add Mulenga Kapwepwe as an actor that is actually receiving the 95 ZMW here, just for clarity?

@MichaelJBRichards
Copy link

Thanks very much for your comments, @henrka and @mdebarros .

On the sequence diagrams:

Conversion by the payee

  1. Editing failure. Fixed
  2. I think it should be added. Now have notification for both FXP and Payee DFSP.
  3. Done
  4. Good plan. Added her

Conversion by the payer

  1. Agree. Removed
  2. Done
  3. Again, added notification for FXP
  4. As you say, the intention is that the switch should check to see if there are any other transfers which depend on the transfer it is processing. I thought that this was a simpler way of structuring things.
  5. Added Mulengwa

Reference currency (per MdB)

  1. Fixed
  2. Fixed
  3. Fixed
  4. Fixed

I will upload new copies of the sequence diagrams to the repository, together with a copy of the new draft of the API and some further sequence diagrams.

@mdebarros
Copy link
Member

mdebarros commented Oct 5, 2023

Some issues with the markdown document (possible conversion issues?):

  1. See points 3,4 & 7 on Assumptions: https://github.com/mojaloop/Currency-conversion/blob/main/APICandidate%20for%20currency%20conversion%20support%20in%20Mojaloop.md#assumptions
  2. See the dangling empty point structure above the Functions of an FXP title: https://github.com/mojaloop/Currency-conversion/blob/main/APICandidate%20for%20currency%20conversion%20support%20in%20Mojaloop.md#functions-of-an-fxp

@PaulGregoryBaker
Copy link

I had a closer look at the optional converter field, and realised that it was no required for the reference currency flow that I was concerned about.
I no longer have any concerns.

@mdebarros
Copy link
Member

mdebarros commented Oct 11, 2023

Also, just adding a comment from todays CCB discussion:

Raised issue on the implementation of the FX specification, specifically that Transfer prepare processing will ALWAYS have to check if there is an associated fxTransfers using the transactionId from the transaction-object.

As discussed this would be resolved in two ways:

  1. The transaction-object will include the converter fields - if they are available in the transaction-object, it will signify that there is an fxTransfer
  2. There is another proposal to include the transact-object as part of the transfers, so no additional look-ups would be required to attain the converter fields and the transactionId

@MichaelJBRichards please review the above comment, and let me know if we require any corrections.

@MichaelJBRichards
Copy link

I have uploaded a revised version of the candidate API document (MD version, natch) to the repository. It addresses comments made by Vijay in his review of the document.

@millerabel
Copy link
Member

millerabel commented Oct 25, 2023

Hmm… one of the original design principles separates the transaction logic from the simple transfer of money. I’m reluctant to start down the slippery slope of consulting the transaction details from the transfer logic. Should it be possible to encode the currency and fx details that are relevant for transfer as instructions to the clearing engine (in the transfer object)? We should not pierce the veil of the transaction object from within the clearing engine.

@mjbrichards
Copy link
Author

In this case there are two (or possibly even three) transaction objects. Well, one (or possibly two) of those are Conversion objects, which are the FX version of a transaction object. The set of objects are available to the switch in the same way as the transaction object is; and the switch checks that all the objects have been received and the associated requests approved before it finalises the payment and records all the obligations. So I don't think that there is a requirement to pierce the veil of the transaction object: it's just that this is a dance of the two (or possibly three) veils.

@millerabel
Copy link
Member

The concept of a Transfer Set seems natural and obvious to me. If the set of related transfers (including the conversions and local transfers) are organized into a set object with deterministic processing rules we build on the Interledger guarantees. That suggests all-or-none, two-phase clearing and commit across the set, but should not necessarily require ordering of transfers.

@mjbrichards
Copy link
Author

And I am happy to report that I think that's just what our proposed switch processing does. I would look forward to explaining how at the convening, but Cici wants to go big on cross-scheme currency conversion scenarios (and I, of course, concur). However, I am available to give, for a small fee, my famed explanation in which transfers are semi-convincingly played by beer bottles.

@millerabel
Copy link
Member

I am all for prototypes using read-at-hand materials, especially when beer is involved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fspiop-change-request A change request for the FSPIOP API ml-ccb CCB issues or issues common to all the SIGs
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

10 participants