10.63. DD 63: LibEufin Conversion Rate Group#

10.63.1. Summary#

We need an efficient solution to set specific conversion rates for some users. By efficient this means you do not have to set or update them manually for all accounts and a good way to see what are the current rates and which accounts use them.

10.63.2. Proposed Solution#

The current global conversion rate schema is:

interface ConversionRate {
  // Minimum fiat amount authorised for cashin before conversion
  cashin_min_amount: Amount;

  // Exchange rate to buy regional currency from fiat
  cashin_ratio: DecimalNumber;

  // Regional amount fee to subtract after applying the cashin ratio.
  cashin_fee: Amount;

  // Smallest possible regional amount, converted amount is rounded to this amount
  cashin_tiny_amount: Amount;

  // Rounding mode used during cashin conversion
  cashin_rounding_mode: "zero" | "up" | "nearest";

  // Minimum regional amount authorised for cashout before conversion
  cashout_min_amount: Amount;

  // Exchange rate to sell regional currency for fiat
  cashout_ratio: DecimalNumber;

  // Fiat amount fee to subtract after applying the cashout ratio.
  cashout_fee: Amount;

  // Smallest possible fiat amount, converted amount is rounded to this amount
  cashout_tiny_amount: Amount;

  // Rounding mode used during cashout conversion
  cashout_rounding_mode: "zero" | "up" | "nearest";
}

This would become the default group, that will be used by all created users. The administrator would be able to create new groups that override some of the default properties:

interface ConversionRateGroupRequest {
  // The name of this group, cannot be default
  name: string;

  // A description of the group
  description?: string;

  // Minimum fiat amount authorised for cashin before conversion
  cashin_min_amount?: Amount;

  // Exchange rate to buy regional currency from fiat
  cashin_ratio?: DecimalNumber;

  // Regional amount fee to subtract after applying the cashin ratio.
  cashin_fee?: Amount;

  // Smallest possible regional amount, converted amount is rounded to this amount
  cashin_tiny_amount?: Amount;

  // Rounding mode used during cashin conversion
  cashin_rounding_mode?: "zero" | "up" | "nearest";

  // Minimum regional amount authorised for cashout before conversion
  cashout_min_amount?: Amount;

  // Exchange rate to sell regional currency for fiat
  cashout_ratio?: DecimalNumber;

  // Fiat amount fee to subtract after applying the cashout ratio.
  cashout_fee?: Amount;

  // Smallest possible fiat amount, converted amount is rounded to this amount
  cashout_tiny_amount?: Amount;

  // Rounding mode used during cashout conversion
  cashout_rounding_mode?: "zero" | "up" | "nearest";
}
interface ConversionRateGroup {
  // The name of this group, default is the default group
  name: string;

  // A description of the group
  description?: string;

  // Group unique ID
  row_id: Integer;

  // Number of users affected to this group
  num_users: Integer;

  // Applied conversion rate
  conversion_rate: ConversionRate;

  // Minimum fiat amount authorised for cashin before conversion
  cashin_min_amount?: Amount;

  // Exchange rate to buy regional currency from fiat
  cashin_ratio?: DecimalNumber;

  // Regional amount fee to subtract after applying the cashin ratio.
  cashin_fee?: Amount;

  // Smallest possible regional amount, converted amount is rounded to this amount
  cashin_tiny_amount?: Amount;

  // Rounding mode used during cashin conversion
  cashin_rounding_mode?: "zero" | "up" | "nearest";

  // Minimum regional amount authorised for cashout before conversion
  cashout_min_amount?: Amount;

  // Exchange rate to sell regional currency for fiat
  cashout_ratio?: DecimalNumber;

  // Fiat amount fee to subtract after applying the cashout ratio.
  cashout_fee?: Amount;

  // Smallest possible fiat amount, converted amount is rounded to this amount
  cashout_tiny_amount?: Amount;

  // Rounding mode used during cashout conversion
  cashout_rounding_mode?: "zero" | "up" | "nearest";
}

When we run the conversion logic we take values from the user group and fallback to the default values when they are null. If the ratio is zero it disable the conversion.

10.63.2.1. Taler Conversion Info API#

We need to move the current conversion-info API from /conversion-info/* to /accounts/$USERNAME/conversion-info/*. We keep the current API to only show the default rate for retro compatibility.

We deprecate POST /conversion-rate to make the API readonly (the Info in the name was a hint).

TODO: How hard is it to migrate to this in the wallets ?

10.63.2.2. Taler Core Bank API#

We migrate POST /conversion-rate here to set the default conversion rate group value.

We add new admin only conversion rate group management endpoints:

POST /conversion-rate-group GET /conversion-rate-group GET /conversion-rate-group/$GROUP-ID PATCH /conversion-rate-group/$GROUP-ID DELETE /conversion-rate-group/$GROUP-ID

add /conversion-rate-group/$GROUP-ID/conversion-info/*

We add admin only conversion_rate_group Integer optional field to POST /accounts and PATCH /accounts/$USERNAME.

We add conversion_rate_group Integer optional field and query filter to GET /accounts We add conversion_rate_group ConversionRateGroup field of GET /accounts/$USERNAME