Skip to content

Commit

Permalink
math
Browse files Browse the repository at this point in the history
  • Loading branch information
leecchh committed Nov 19, 2024
1 parent 1b5d8cb commit 2c94f88
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 37 deletions.
39 changes: 24 additions & 15 deletions packages/payments/sources/payments.move
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const ECannotUseOracleForBaseCurrency: vector<u8> =
const EPriceFeedIdMismatch: vector<u8> =
b"The supplied `PriceInfoObject` is invalid for the given coin type.";

const BUFFER: u8 = 10;

/// Configuration for the payments module.
/// Holds a VecMap that determines the configuration for each currency.
public struct PaymentsConfig has store, drop {
Expand All @@ -42,7 +44,8 @@ public struct CoinTypeData has store, drop {
decimals: u8,
// A discount can be applied if the user pays with this currency.
discount_percentage: u8,
// Pyth's price feed id for the given currency. Make sure you omit the `0x` prefix.
// Pyth's price feed id for the given currency. Make sure you omit the `0x`
// prefix.
price_feed_id: vector<u8>,
}

Expand Down Expand Up @@ -95,20 +98,26 @@ public fun handle_payment<T>(
);

// The amount that has to be paid in base currency.
let base_currency_price = intent.request_data().base_amount();

// Now we need to calculate the amount of `T` coins that the user needs to pay.
// We know that the price we'll be getting is relative to the base currency
// (In our scenario, USD)
let decimal_i64 = price.get_expo();
let price = price.get_price();

// get the u64 price of SUI.
let price_u64 = price.get_magnitude_if_positive();


// TODO: Calculate the price, and if payment is sufficient, finalize or abort.
// Math needed :)
let base_currency_amount = intent.request_data().base_amount();

let target_decimals = config.currencies.get(&payment_type).decimals;
let base_decimals = config.currencies.get(&config.base_currency).decimals;
let pyth_decimals = price.get_expo().get_magnitude_if_negative() as u8;
let pyth_price = price.get_price().get_magnitude_if_positive();

// TODO: Optionally assert confidence is high enough?
// let pyth_confidence = price.get_conf();

let exponent_with_buffer =
BUFFER + target_decimals + pyth_decimals - base_decimals;
let target_currency_amount =
(base_currency_amount as u128 * 10u128.pow(exponent_with_buffer as u8))
.divide_and_round_up(pyth_price as u128)
.divide_and_round_up(10u128.pow(BUFFER as u8)) as u64;

// TODO: What if oracle price is volatile? Should we have a read only
// function for the currency amount needed?
assert!(payment.value() == target_currency_amount, EInsufficientPayment);

intent.finalize_payment(suins, PaymentsApp(), payment)
}
Expand Down
61 changes: 39 additions & 22 deletions packages/payments/tests/payments_tests.move
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@

// module payments::payments_tests;

// use std::u64;

// #[test]
// fun test_ugly_math() {

// let chain_decimals = 6;
// let pyth_decimals = 8; // -8 equivalent
// let target_decimals = 6;

// let pyth_price = 37017259;

// let exponent = 0;

// let val = (2_000_000 * 1_000_000 * 10u64.pow(exponent as u8)).divide_and_round_up(pyth_price);

// std::debug::print(&val);

// // 10^(chain_decimal_ns - pyth_expo - usdc_decimal) / pyth_price * x
// }
module payments::payments_tests;

#[test]
fun test_math() {
let buffer = 10;
let target_decimals: u8 = 9;
let base_decimals: u8 = 6;
let pyth_decimals: u8 = 8;
let pyth_price = 380000000; // SUI price 3.8
let base_currency_amount = 100 * 1_000_000; // 100 USDC

let exponent_with_buffer =
buffer + target_decimals + pyth_decimals - base_decimals;
let target_currency_amount =
(base_currency_amount as u128 * 10u128.pow(exponent_with_buffer as u8))
.divide_and_round_up(pyth_price as u128)
.divide_and_round_up(10u128.pow(buffer as u8)) as u64;

assert!(target_currency_amount == 26315789474, 1); // 26.315789474 SUI
}

#[test]
fun test_math_2() {
let buffer = 10;
let target_decimals: u8 = 0; // TOKEN has no decimals
let base_decimals: u8 = 6;
let pyth_decimals: u8 = 3;
let pyth_price = 3800; // TOKEN price 3.8
let base_currency_amount = 100 * 1_000_000; // 100 USDC

let exponent_with_buffer =
buffer + target_decimals + pyth_decimals - base_decimals;
let target_currency_amount =
(base_currency_amount as u128 * 10u128.pow(exponent_with_buffer as u8))
.divide_and_round_up(pyth_price as u128)
.divide_and_round_up(10u128.pow(buffer as u8)) as u64;

assert!(target_currency_amount == 27, 1); // 27 TOKEN
}

0 comments on commit 2c94f88

Please sign in to comment.