Closed mistermoe closed 9 months ago
would balances offer something different to what exchanges would if you reconciled them? ie is it sort of like a materialised view of it or is there more functionality?
@phoebe-lew and i synced and came up with the following proposal:
At the protocol level, we propose to introduce a specific PaymentMethod
kind
named STORED_BALANCE
. For example:
to purchase USDC that is custodied with the PFI using USD (aka USD -> USDC) the offering would appear as:
[!NOTE] omitted properties for brevity
{
"payinCurrency": "USD",
"payoutCurrency": "USDC",
"payinMethods": [{
"kind": "DEBIT_CARD"
}],
"payoutMethods": [{
"kind": "STORED_BALANCE"
}]
}
on the flip side, to utilize a stored balance held with a PFI, an offering would appear as:
{
"payinCurrency": "USDC",
"payoutCurrency": "MXN",
"payinMethods": [{
"kind": "STORED_BALANCE"
}],
"payoutMethods": [{
"kind": "BANK_ACCT"
}]
}
Accessing balances from a PFI can be achieved via a secured endpoint:
GET /balances
Authorization: Bearer ${requestToken}
Here, requestToken
is created using the same method as employed for retrieving exchanges (aka GET /exchanges
).
The response body would be structured as follows:
{
"balances": [{
"currency": "USDC",
"available": "100"
}]
}
expressed as types:
type GetBalancesResponse = {
balances: Balance[]
}
type Balance = {
/** ISO 4217 currency code or widely adopted cryptocurrency code */
currency: string
/** same format used to represent currency values across messages */
available: string
}
this approach supports both institutional top ups in addition to the ability to custody funds for retail customers. For example:
[!NOTE]
requiredClaims
value has been abbreviated to keep the example terse
a USD -> USDC institutional top-up offering could appear as:
{
"payinCurrency": "USD",
"payoutCurrency": "USDC",
"payinMethods": [{
"kind": "WIRE_TRANSFER"
}],
"payoutMethods": [{
"kind": "STORED_BALANCE"
}],
"requiredClaims": ["KnownBusinessCredential"]
}
a USD -> USDC retail customer offering could appear as:
{
"payinCurrency": "USD",
"payoutCurrency": "USDC",
"payinMethods": [{
"kind": "WIRE_TRANSFER"
}],
"payoutMethods": [{
"kind": "STORED_BALANCE"
}],
"requiredClaims": ["KnownCustomerCredential"]
}
utilizing the stored balance would occur in the same way described in the first example set.
Supporting stored balances is entirely optional. The intent is to provide the ability for PFIs who choose to do so. PFIs that do not support this functionality can respond to GET /balances
with a 404: Not Found
.
props to @phoebe-lew for coming up with the reserved kind
approach
cc: @corcillo
would balances offer something different to what exchanges would if you reconciled them? ie is it sort of like a materialised view of it or is there more functionality?
nope you got it! technically no additional functionality needed though likely but not prescribed in any way at the protocol or api spec level
Further, the rationale for Balances
being an array is that there are n-number of currencies that each customer could have balances in. The amount categories (just available
for now) are also extensible in case there's a use case for a customer wanting to see their overall pending
amount.
Main question i have, and i probably could think it through on my own but will just state for fun and completeness , what does it look like when an institution that has topped up sends in a message request for one of their customers to do an offramp?
On Tue, Jan 9, 2024 at 10:06 PM Moe Jangda @.***> wrote:
@phoebe-lew https://github.com/phoebe-lew and i synced and came up with the following proposal:
At the protocol level, we propose to introduce a specific PaymentMethod kind named STORED_BALANCE. For example:
to purchase USDC that is custodied with the PFI using USD (aka USD -> USDC) the offering would appear as:
Note
omitted properties for brevity
{ "payinCurrency": "USD", "payoutCurrency": "USDC", "payinMethods": [{ "kind": "DEBIT_CARD" }], "payoutMethods": [{ "kind": "STORED_BALANCE" }] }
on the flip side, to utilize a stored balance held with a PFI, an offering would appear as:
{ "payinCurrency": "USDC", "payoutCurrency": "MXN", "payinMethods": [{ "kind": "STORED_BALANCE" }], "payoutMethods": [{ "kind": "BANK_ACCT" }] }
Accessing balances from a PFI can be achieved via a secured endpoint:
GET /balances Authorization: Bearer ${requestToken}
Here, requestToken is created using the same method as employed for retrieving exchanges (aka GET /exchanges).
The response body would be structured as follows:
{ "balances": [{ "currency": "USDC", "available": "100" }] }
expressed as types:
type GetBalancesResponse = { balances: Balance[]} type Balance = { /* ISO 4217 currency code or widely adopted cryptocurrency code / currency: string /* same format used to represent currency values across messages / available: string}
this approach supports both institutional top ups in addition to the ability to custody funds for retail customers. For example:
Note
requiredClaims value has been abbreviated to keep the example terse
a USD -> USDC institutional top-up offering could appear as:
{ "payinCurrency": "USD", "payoutCurrency": "USDC", "payinMethods": [{ "kind": "WIRE_TRANSFER" }], "payoutMethods": [{ "kind": "STORED_BALANCE" }], "requiredClaims": ["KnownBusinessCredential"] }
a USD -> USDC retail customer offering could appear as:
{ "payinCurrency": "USD", "payoutCurrency": "USDC", "payinMethods": [{ "kind": "WIRE_TRANSFER" }], "payoutMethods": [{ "kind": "STORED_BALANCE" }], "requiredClaims": ["KnownCustomerCredential"] }
utilizing the stored balance would occur in the same way described in the first example set.
Supporting stored balances is entirely optional. The intent is to provide the ability for PFIs who choose to do so. PFIs that do not support this functionality can respond to GET /balances with a 404: Not Found .
props to @phoebe-lew https://github.com/phoebe-lew for coming up with the reserved kind approach
cc: @corcillo https://github.com/corcillo
— Reply to this email directly, view it on GitHub https://github.com/TBD54566975/tbdex/issues/209#issuecomment-1884242994, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABC2ZO2HKM5X6N44YASCKMTYNYVVVAVCNFSM6AAAAABA2HVEW2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTQOBUGI2DEOJZGQ . You are receiving this because you were mentioned.Message ID: @.***>
@corcillo So let's say a DIDpay customer is offramping USDC -> MXN. The offering looks like:
{
"payinCurrency": "USDC",
"payoutCurrency": "MXN",
"payinMethods": [{
"kind": "STORED_BALANCE"
}],
"payoutMethods": [{
"kind": "SPEI"
}],
"requiredClaims": ["SanctionsCredential"]
}
Because it's drawing against DIDpay's stored balance with the PFI. The wallet app can associate the exchange to their own internal transaction id via the external_id
field: https://github.com/orgs/TBD54566975/projects/29/views/1?pane=issue&itemId=44614947
Formally, the concept of an "account" does not exist at the tbdex layer nor is it necessary IMO. The concept of an account however may be beneficial with respect to PFIs. An account needn't be anything complicated. They can be represented as individual DIDs. Technically this already exists because
GET /exchanges
is effectively returning all exchanges for a given "account" where that account is represented as a DID.an endpoint like
GET /balances
could be used to fetch balances that Alice has with a PFI.For example, If a PFI had a
USD -> USDC_STORED_BALANCE
offering, Alice would be purchasing USDC to be custodied with the PFI. Alice could then useGET /balances
to get her balance.The PFI could then have another offering like
USDC_STORED_BALANCE -> MXN
which could be used to off-ramp her stored balance.This also provides a standarized means to facilitate institutional top-ups