Smart Contract

EVMAmountUtils

A.43c9e8bfec507db4.EVMAmountUtils

Valid From

142,038,852

Deployed

2w ago
Feb 13, 2026, 02:43:00 AM UTC

Dependents

0 imports
1import FlowEVMBridgeUtils from 0x1e4aa0b87d10b141
2import EVM from 0xe467b9dd11fa00df
3
4/// EVMAmountUtils
5///
6/// Shared utility contract for precision-safe EVM ↔ Cadence UFix64 amount conversions.
7///
8/// EVM tokens can have up to 18 decimal places, while Cadence UFix64 only supports 8.
9/// Converting naively truncates lower-order digits, which can cause rounding errors in
10/// DeFi operations. These helpers apply directional rounding:
11///
12/// - `toCadenceOut` (round **down**): safe for **output** amounts — user receives at most this much
13/// - `toCadenceIn` (round **up**): safe for **input** amounts — user must provide at least this much
14///
15access(all) contract EVMAmountUtils {
16
17    /// Convert an ERC20 `UInt256` amount into a Cadence `UFix64` **by rounding down** to the
18    /// maximum `UFix64` precision (8 decimal places).
19    ///
20    /// - For `decimals <= 8`, the value is exactly representable, so this is a direct conversion.
21    /// - For `decimals > 8`, this floors the ERC20 amount to the nearest multiple of
22    ///   `quantum = 10^(decimals - 8)` so the result round-trips safely:
23    ///   `ufix64ToUInt256(result) <= amt`.
24    access(all) fun toCadenceOut(_ amt: UInt256, decimals: UInt8): UFix64 {
25        if decimals <= 8 {
26            return FlowEVMBridgeUtils.uint256ToUFix64(value: amt, decimals: decimals)
27        }
28
29        let quantumExp: UInt8 = decimals - 8
30        let quantum: UInt256 = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)
31        let remainder: UInt256 = amt % quantum
32        let floored: UInt256 = amt - remainder
33
34        return FlowEVMBridgeUtils.uint256ToUFix64(value: floored, decimals: decimals)
35    }
36
37    /// Convert an ERC20 `UInt256` amount into a Cadence `UFix64` **by rounding up** to the
38    /// smallest representable value at `UFix64` precision (8 decimal places).
39    ///
40    /// - For `decimals <= 8`, the value is exactly representable, so this is a direct conversion.
41    /// - For `decimals > 8`, this ceils the ERC20 amount to the next multiple of
42    ///   `quantum = 10^(decimals - 8)` (unless already exact), ensuring:
43    ///   `ufix64ToUInt256(result) >= amt`, and the increase is `< quantum`.
44    access(all) fun toCadenceIn(_ amt: UInt256, decimals: UInt8): UFix64 {
45        if decimals <= 8 {
46            return FlowEVMBridgeUtils.uint256ToUFix64(value: amt, decimals: decimals)
47        }
48
49        let quantumExp: UInt8 = decimals - 8
50        let quantum: UInt256 = FlowEVMBridgeUtils.pow(base: 10, exponent: quantumExp)
51
52        let remainder: UInt256 = amt % quantum
53        var padded: UInt256 = amt
54        if remainder != 0 {
55            padded = amt + (quantum - remainder)
56        }
57
58        return FlowEVMBridgeUtils.uint256ToUFix64(value: padded, decimals: decimals)
59    }
60
61    /// Convenience: resolve token decimals and round down for output amounts
62    access(all) fun toCadenceOutForToken(_ amt: UInt256, erc20Address: EVM.EVMAddress): UFix64 {
63        let decimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: erc20Address)
64        return self.toCadenceOut(amt, decimals: decimals)
65    }
66
67    /// Convenience: resolve token decimals and round up for input amounts
68    access(all) fun toCadenceInForToken(_ amt: UInt256, erc20Address: EVM.EVMAddress): UFix64 {
69        let decimals = FlowEVMBridgeUtils.getTokenDecimals(evmContractAddress: erc20Address)
70        return self.toCadenceIn(amt, decimals: decimals)
71    }
72
73    init() {}
74}
75