Skip to content

Commit e6e431a

Browse files
authored
Merge pull request #23 from axieinfinity/merge/release/v0.1.0-feature/math
chore(`math`): merge from `release/v0.1.0`
2 parents 6895647 + d9060b4 commit e6e431a

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

src/LibErrorHandler.sol

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.19;
3+
4+
library LibErrorHandler {
5+
/// @dev Reserves error definition to upload to signature database.
6+
error ExternalCallFailed(bytes4 msgSig, bytes4 callSig);
7+
8+
/// @notice handle low level call revert if call failed,
9+
/// If extcall return empty bytes, reverts with custom error.
10+
/// @param status Status of external call
11+
/// @param callSig function signature of the calldata
12+
/// @param returnOrRevertData bytes result from external call
13+
function handleRevert(bool status, bytes4 callSig, bytes memory returnOrRevertData) internal pure {
14+
// Get the function signature of current context
15+
bytes4 msgSig = msg.sig;
16+
assembly ("memory-safe") {
17+
if iszero(status) {
18+
// Load the length of bytes array
19+
let revertLength := mload(returnOrRevertData)
20+
// Check if length != 0 => revert following reason from external call
21+
if iszero(iszero(revertLength)) {
22+
// Start of revert data bytes. The 0x20 offset is always the same.
23+
revert(add(returnOrRevertData, 0x20), revertLength)
24+
}
25+
26+
// Load free memory pointer
27+
let ptr := mload(0x40)
28+
// Store 4 bytes the function selector of ExternalCallFailed(msg.sig, callSig)
29+
// Equivalent to revert ExternalCallFailed(bytes4,bytes4)
30+
mstore(ptr, 0x49bf4104)
31+
// Store 4 bytes of msgSig parameter in the next slot
32+
mstore(add(ptr, 0x20), msgSig)
33+
// Store 4 bytes of callSig parameter in the next slot
34+
mstore(add(ptr, 0x40), callSig)
35+
// Revert 68 bytes of error starting from 0x1c
36+
revert(add(ptr, 0x1c), 0x44)
37+
}
38+
}
39+
}
40+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import { LibErrorHandler } from "../LibErrorHandler.sol";
5+
6+
/**
7+
* @title NativeTransferHelper
8+
*/
9+
library LibNativeTransfer {
10+
using LibErrorHandler for bool;
11+
12+
/**
13+
* @dev Transfers Native Coin and wraps result for the method caller to a recipient.
14+
*/
15+
function transfer(address to, uint256 value, uint256 gasAmount) internal {
16+
(bool success, bytes memory returnOrRevertData) = trySendValue(to, value, gasAmount);
17+
success.handleRevert(bytes4(0x0), returnOrRevertData);
18+
}
19+
20+
/**
21+
* @dev Unsafe send `amount` Native to the address `to`. If the sender's balance is insufficient,
22+
* the call does not revert.
23+
*
24+
* Note:
25+
* - Does not assert whether the balance of sender is sufficient.
26+
* - Does not assert whether the recipient accepts NATIVE.
27+
* - Consider using `ReentrancyGuard` before calling this function.
28+
*
29+
*/
30+
function trySendValue(address to, uint256 value, uint256 gasAmount)
31+
internal
32+
returns (bool success, bytes memory returnOrRevertData)
33+
{
34+
(success, returnOrRevertData) = to.call{ value: value, gas: gasAmount }("");
35+
}
36+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.23;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
import { LibNativeTransfer } from "src/transfers/LibNativeTransfer.sol";
6+
7+
contract LibNativeTransferTest is Test {
8+
function testFork_RevertWhen_TransferNativeToContractWithoutFallback_safeTransfer(
9+
address any,
10+
uint256 amount,
11+
uint256 gas
12+
) external {
13+
vm.deal(any, amount);
14+
vm.expectRevert();
15+
vm.prank(any);
16+
LibNativeTransfer.transfer(address(this), amount, gas);
17+
}
18+
19+
function testConcrete_TransferNative(uint256 gas) external {
20+
LibNativeTransfer.transfer(address(0xBEEF), 1e18, gas);
21+
assertEq(address(0xBEEF).balance, 1e18);
22+
}
23+
24+
function testFork_TransferNativeToRecipient(address recipient, uint256 amount, uint256 gas) external {
25+
// Transferring to msg.sender can fail because it's possible to overflow their ETH balance as it begins non-zero.
26+
if (recipient.code.length > 0 || uint256(uint160(recipient)) <= 18 || recipient == msg.sender) return;
27+
28+
amount = bound(amount, 0, address(this).balance);
29+
LibNativeTransfer.transfer(recipient, amount, gas);
30+
31+
assertEq(recipient.balance, amount);
32+
}
33+
}

0 commit comments

Comments
 (0)