@@ -19,9 +19,7 @@ mod fixtures;
19
19
mod message_service;
20
20
#[ cfg( test) ]
21
21
mod test_helpers;
22
- use log:: debug;
23
22
use num_bigint:: BigUint ;
24
- use num_traits:: cast:: ToPrimitive ;
25
23
use std:: ops:: { Div , Mul } ;
26
24
27
25
pub use api:: SettlementApi ;
@@ -47,6 +45,10 @@ impl Quantity {
47
45
}
48
46
}
49
47
48
+ // TODO: Since we still haven't finalized all the settlement details, we might
49
+ // end up deciding to add some more values, e.g. some settlement engine uid or similar.
50
+ // All instances of this struct should be replaced with Url instances once/if we
51
+ // agree that there is no more info required to refer to an engine.
50
52
pub struct SettlementEngineDetails {
51
53
/// Base URL of the settlement engine
52
54
pub url : Url ,
@@ -104,53 +106,61 @@ pub struct ConvertDetails {
104
106
105
107
/// Traits for u64 and f64 asset code conversions for amounts and rates
106
108
pub trait Convert {
107
- fn normalize_scale ( & self , details : ConvertDetails ) -> Self ;
109
+ type Item : Sized ;
110
+
111
+ // Returns the scaled result, or an error if there was an overflow
112
+ fn normalize_scale ( & self , details : ConvertDetails ) -> Result < Self :: Item , ( ) > ;
108
113
}
109
114
110
115
impl Convert for u64 {
111
- fn normalize_scale ( & self , details : ConvertDetails ) -> Self {
116
+ type Item = u64 ;
117
+
118
+ fn normalize_scale ( & self , details : ConvertDetails ) -> Result < Self :: Item , ( ) > {
112
119
let scale_diff = ( details. from as i8 - details. to as i8 ) . abs ( ) as u8 ;
113
120
let scale = 10u64 . pow ( scale_diff. into ( ) ) ;
114
- let num = BigUint :: from ( * self ) ;
115
- let num = if details. to >= details. from {
116
- num. mul ( scale)
121
+ let ( res, overflow) = if details. to >= details. from {
122
+ self . overflowing_mul ( scale)
117
123
} else {
118
- num . div ( scale)
124
+ self . overflowing_div ( scale)
119
125
} ;
120
- if let Some ( num_u64 ) = num . to_u64 ( ) {
121
- num_u64
126
+ if overflow {
127
+ Err ( ( ) )
122
128
} else {
123
- debug ! (
124
- "Overflow during conversion from {} {:?}. Using u64::MAX" ,
125
- num, details
126
- ) ;
127
- std:: u64:: MAX
129
+ Ok ( res)
128
130
}
129
131
}
130
132
}
131
133
132
134
impl Convert for f64 {
135
+ type Item = f64 ;
133
136
// Not overflow safe. Would require using a package for Big floating point
134
137
// numbers such as BigDecimal
135
- fn normalize_scale ( & self , details : ConvertDetails ) -> Self {
138
+ fn normalize_scale ( & self , details : ConvertDetails ) -> Result < Self :: Item , ( ) > {
136
139
let scale_diff = ( details. from as i8 - details. to as i8 ) . abs ( ) as u8 ;
137
140
let scale = 10f64 . powi ( scale_diff. into ( ) ) ;
138
- if details. to >= details. from {
141
+ let res = if details. to >= details. from {
139
142
self * scale
140
143
} else {
141
144
self / scale
145
+ } ;
146
+ if res == std:: f64:: INFINITY {
147
+ Err ( ( ) )
148
+ } else {
149
+ Ok ( res)
142
150
}
143
151
}
144
152
}
145
153
146
154
impl Convert for BigUint {
147
- fn normalize_scale ( & self , details : ConvertDetails ) -> Self {
155
+ type Item = BigUint ;
156
+
157
+ fn normalize_scale ( & self , details : ConvertDetails ) -> Result < Self :: Item , ( ) > {
148
158
let scale_diff = ( details. from as i8 - details. to as i8 ) . abs ( ) as u8 ;
149
159
let scale = 10u64 . pow ( scale_diff. into ( ) ) ;
150
160
if details. to >= details. from {
151
- self . mul ( scale)
161
+ Ok ( self . mul ( scale) )
152
162
} else {
153
- self . div ( scale)
163
+ Ok ( self . div ( scale) )
154
164
}
155
165
}
156
166
}
@@ -162,93 +172,156 @@ mod tests {
162
172
163
173
#[ test]
164
174
fn u64_test ( ) {
165
- // does not overflow
175
+ // overflows
166
176
let huge_number = std:: u64:: MAX / 10 ;
167
177
assert_eq ! (
168
- huge_number. normalize_scale( ConvertDetails { from: 1 , to: 18 } ) ,
169
- std:: u64 :: MAX
178
+ huge_number
179
+ . normalize_scale( ConvertDetails { from: 1 , to: 18 } )
180
+ . unwrap_err( ) ,
181
+ ( ) ,
170
182
) ;
171
183
// 1 unit with scale 1, is 1 unit with scale 1
172
- assert_eq ! ( 1u64 . normalize_scale( ConvertDetails { from: 1 , to: 1 } ) , 1 ) ;
184
+ assert_eq ! (
185
+ 1u64 . normalize_scale( ConvertDetails { from: 1 , to: 1 } )
186
+ . unwrap( ) ,
187
+ 1
188
+ ) ;
173
189
// there's leftovers for all number slots which do not increase in
174
190
// increments of 10^abs(to_scale-from_scale)
175
- assert_eq ! ( 1u64 . normalize_scale( ConvertDetails { from: 2 , to: 1 } ) , 0 ) ;
191
+ assert_eq ! (
192
+ 1u64 . normalize_scale( ConvertDetails { from: 2 , to: 1 } )
193
+ . unwrap( ) ,
194
+ 0
195
+ ) ;
176
196
// 1 unit with scale 1, is 10 units with scale 2
177
- assert_eq ! ( 1u64 . normalize_scale( ConvertDetails { from: 1 , to: 2 } ) , 10 ) ;
197
+ assert_eq ! (
198
+ 1u64 . normalize_scale( ConvertDetails { from: 1 , to: 2 } )
199
+ . unwrap( ) ,
200
+ 10
201
+ ) ;
178
202
// 1 gwei (scale 9) is 1e9 wei (scale 18)
179
203
assert_eq ! (
180
- 1u64 . normalize_scale( ConvertDetails { from: 9 , to: 18 } ) ,
204
+ 1u64 . normalize_scale( ConvertDetails { from: 9 , to: 18 } )
205
+ . unwrap( ) ,
181
206
1_000_000_000
182
207
) ;
183
208
// 1_000_000_000 wei is 1gwei
184
209
assert_eq ! (
185
- 1_000_000_000u64 . normalize_scale( ConvertDetails { from: 18 , to: 9 } ) ,
210
+ 1_000_000_000u64
211
+ . normalize_scale( ConvertDetails { from: 18 , to: 9 } )
212
+ . unwrap( ) ,
186
213
1 ,
187
214
) ;
188
215
// 10 units with base 2 is 100 units with base 3
189
216
assert_eq ! (
190
- 10u64 . normalize_scale( ConvertDetails { from: 2 , to: 3 } ) ,
217
+ 10u64
218
+ . normalize_scale( ConvertDetails { from: 2 , to: 3 } )
219
+ . unwrap( ) ,
191
220
100
192
221
) ;
193
222
// 299 units with base 3 is 29 units with base 2 (0.9 leftovers)
194
223
assert_eq ! (
195
- 299u64 . normalize_scale( ConvertDetails { from: 3 , to: 2 } ) ,
224
+ 299u64
225
+ . normalize_scale( ConvertDetails { from: 3 , to: 2 } )
226
+ . unwrap( ) ,
196
227
29
197
228
) ;
198
- assert_eq ! ( 999u64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) , 0 ) ;
199
229
assert_eq ! (
200
- 1000u64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) ,
230
+ 999u64
231
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
232
+ . unwrap( ) ,
233
+ 0
234
+ ) ;
235
+ assert_eq ! (
236
+ 1000u64
237
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
238
+ . unwrap( ) ,
201
239
1
202
240
) ;
203
241
assert_eq ! (
204
- 1999u64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) ,
242
+ 1999u64
243
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
244
+ . unwrap( ) ,
205
245
1
206
246
) ;
207
247
}
208
248
209
249
#[ allow( clippy:: float_cmp) ]
210
250
#[ test]
211
251
fn f64_test ( ) {
252
+ // overflow
253
+ assert_eq ! (
254
+ std:: f64 :: MAX
255
+ . normalize_scale( ConvertDetails {
256
+ from: 1 ,
257
+ to: std:: u8 :: MAX ,
258
+ } )
259
+ . unwrap_err( ) ,
260
+ ( )
261
+ ) ;
262
+
212
263
// 1 unit with base 1, is 1 unit with base 1
213
- assert_eq ! ( 1f64 . normalize_scale( ConvertDetails { from: 1 , to: 1 } ) , 1.0 ) ;
264
+ assert_eq ! (
265
+ 1f64 . normalize_scale( ConvertDetails { from: 1 , to: 1 } )
266
+ . unwrap( ) ,
267
+ 1.0
268
+ ) ;
214
269
// 1 unit with base 10, is 10 units with base 1
215
270
assert_eq ! (
216
- 1f64 . normalize_scale( ConvertDetails { from: 1 , to: 2 } ) ,
271
+ 1f64 . normalize_scale( ConvertDetails { from: 1 , to: 2 } )
272
+ . unwrap( ) ,
217
273
10.0
218
274
) ;
219
275
// 1 sat is 1e9 wei (multiplied by rate)
220
276
assert_eq ! (
221
- 1f64 . normalize_scale( ConvertDetails { from: 9 , to: 18 } ) ,
277
+ 1f64 . normalize_scale( ConvertDetails { from: 9 , to: 18 } )
278
+ . unwrap( ) ,
222
279
1_000_000_000.0
223
280
) ;
224
281
225
282
// 1.0 unit with base 2 is 0.1 unit with base 1
226
- assert_eq ! ( 1f64 . normalize_scale( ConvertDetails { from: 2 , to: 1 } ) , 0.1 ) ;
227
283
assert_eq ! (
228
- 10.5f64 . normalize_scale( ConvertDetails { from: 2 , to: 1 } ) ,
284
+ 1f64 . normalize_scale( ConvertDetails { from: 2 , to: 1 } )
285
+ . unwrap( ) ,
286
+ 0.1
287
+ ) ;
288
+ assert_eq ! (
289
+ 10.5f64
290
+ . normalize_scale( ConvertDetails { from: 2 , to: 1 } )
291
+ . unwrap( ) ,
229
292
1.05
230
293
) ;
231
294
// 100 units with base 3 is 10 units with base 2
232
295
assert_eq ! (
233
- 100f64 . normalize_scale( ConvertDetails { from: 3 , to: 2 } ) ,
296
+ 100f64
297
+ . normalize_scale( ConvertDetails { from: 3 , to: 2 } )
298
+ . unwrap( ) ,
234
299
10.0
235
300
) ;
236
301
// 299 units with base 3 is 29.9 with base 2
237
302
assert_eq ! (
238
- 299f64 . normalize_scale( ConvertDetails { from: 3 , to: 2 } ) ,
303
+ 299f64
304
+ . normalize_scale( ConvertDetails { from: 3 , to: 2 } )
305
+ . unwrap( ) ,
239
306
29.9
240
307
) ;
241
308
242
309
assert_eq ! (
243
- 999f64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) ,
310
+ 999f64
311
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
312
+ . unwrap( ) ,
244
313
0.999
245
314
) ;
246
315
assert_eq ! (
247
- 1000f64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) ,
316
+ 1000f64
317
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
318
+ . unwrap( ) ,
248
319
1.0
249
320
) ;
250
321
assert_eq ! (
251
- 1999f64 . normalize_scale( ConvertDetails { from: 9 , to: 6 } ) ,
322
+ 1999f64
323
+ . normalize_scale( ConvertDetails { from: 9 , to: 6 } )
324
+ . unwrap( ) ,
252
325
1.999
253
326
) ;
254
327
}
0 commit comments