wormhole-foundation / example-native-token-transfers

Open, flexible, and composable framework for transferring tokens across blockchains without liquidity pools
Other
20 stars 39 forks source link

evm+solana: Add overflow checks on sender for amounts that cannot be untrimmed by receiver #268

Open johnsaigle opened 8 months ago

johnsaigle commented 8 months ago
          Agree about adding explicit overflow checks here.

Another check we should consider adding is on the sending side, before the transfer is sent, ensuring the recipient will be able to untrim the amount to its desired decimals. The contracts know their peers' decimals, so they could perform this check before sending out. This way, the transaction would revert on the sending side, not the receiving side in case the target chain wouldn't be able to handle the amount.

Originally posted by @kcsongor in https://github.com/wormhole-foundation/example-native-token-transfers/issues/266#issuecomment-1981002675

djb15 commented 7 months ago

@johnsaigle what's your view on this issue? What's the range of decimals and word sizes over which this issue could manifest?

johnsaigle commented 7 months ago

I gave some examples here: https://github.com/wormhole-foundation/example-native-token-transfers/issues/266#issue-2171615766

The dominant variable is the difference between to_decimals and from_decimals. The greater this difference, the smaller the amount must be. Conversely, larger amounts only work if the platforms have similar numbers of decimals.

Here's some example Python you can play with to get an idea. Using uint64 because it represents Solana and many other chains. uint128 is more appropriate for Solidity though we do a lot of conversions/truncations in the codebase.

>>> int(10e18).bit_length() # This number fits in uint64
64
>>> int(2*10e18).bit_length() # this number overflows uint64
65
>>> amount = 10e6
>>> to_decimals = 18
>>> from_decimals = 6
>>> to_decimals - from_decimals
12
>>> scaling_factor = 10e12 # see the `scale()` functions in EVM and Rust implementations
>>> amount * scaling_factor
1e+20
>>> int(amount * scaling_factor).bit_length() # The amount when scaled in `trimmed_amount` overflows uint64
67