Smart Contract
EVMAmountUtils
A.43c9e8bfec507db4.EVMAmountUtils
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