1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use crate::scrt::cosmwasm_std::StdResult;
use super::uint256::Uint256;

/// Convert between tokens with different decimals.
///
/// # Arguments
///
/// * `amount` - the amount of input token to convert
/// * `rate` - corresponds to the output token decimals. E.g: If we want 1:1 rate and the output token has 6 decimals, then rate = 1_000_000
/// * `input_decimals` - the number of decimals of the input token
/// * `output_decimals` - the number of decimals of the output token
pub fn convert_token(
    amount: impl Into<Uint256>,
    rate: impl Into<Uint256>,
    input_decimals: u8,
    output_decimals: u8
) -> StdResult<Uint256> {
    // result = amount * rate / one whole output token
    let amount = amount.into();
    let rate = rate.into();

    let result = (amount * rate)?;

    // But, if tokens have different number of decimals, we need to compensate either by 
    // dividing or multiplying (depending on which token has more decimals) by the difference.
    // However, we can combine this and the last operation by simply dividing by the input decimals
    // if there is a difference.
    let compensation = if input_decimals == output_decimals {
        output_decimals
    } else {
        input_decimals
    };

    let whole_token = Uint256::from(one_token(compensation));
    let result = Uint256::from(result.0 / whole_token.0);

    Ok(result)
}

/// Get the amount needed to represent 1 whole token given its decimals.
/// Ex. Given token A that has 3 decimals, 1 A == 1000
#[inline]
pub fn one_token(decimals: u8) -> u128 {
    1 * 10u128.pow(decimals.into())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_convert_token() {
        // Assuming the user friendly (in the UI) exchange rate has been set to
        // 1 swapped_token (9 decimals) == 1.5 input_token (9 decimals):
        // the rate would be 1 / 1.5 = 0.(6) or 666666666 (0.(6) ** 10 * 9)
        // meaning the price for 1 whole swapped_token is
        // 1500000000 (1.5 * 10 ** 9 decimals) of input_token.

        // If we want to get 2 of swapped_token, we need to send 3 input_token
        // i.e. amount = 3000000000 (3 * 10 ** 9 decimals)

        let rate = 666_666_666;
        let amount = 3_000_000_000u128;

        let result = convert_token(amount, rate, 9, 9).unwrap();
        assert_eq!(result, 1_999_999_998u128.into());

        // Should work the same even if input_token has less decimals (ex. 6)
        // Here amount has 3 zeroes less because input_token now has 6 decimals, so
        // 1 input_token = 3000000 (3 * 10 ** 6)

        let rate = 666_666_666;
        let amount = 3_000_000;

        let result = convert_token(amount, rate, 6, 9).unwrap();
        assert_eq!(result, 1_999_999_998.into());

        // And the other way around - when swap_token has 6 decimals.
        // Here the rate and result have 3 less digits - to account for the less decimals

        let rate = 666_666;
        let amount = 3_000_000_000u64;

        let result = convert_token(amount, rate, 9, 6).unwrap();
        assert_eq!(result, 1_999_998.into());

        let rate = 150000000;
        let amount = 5 * one_token(18);

        let result = convert_token(amount, rate, 18, 8).unwrap();
        assert_eq!(result, 7_5_000_000_0.into());

        let rate = 15 * one_token(17); // 1.5
        let amount = 5 * one_token(8);

        let result = convert_token(amount, rate, 8, 18).unwrap();
        assert_eq!(result, (75 * one_token(17)).into()); // 7.5
    }
}