Created
December 24, 2021 17:32
-
-
Save z0r0z/8dc2d71c6ab862e297fca400486e82bd to your computer and use it in GitHub Desktop.
multi asset multi account multi deadline vesting escrow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // SPDX-License-Identifier: GPL-3.0-or-later | |
| pragma solidity >=0.8.4; | |
| /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. | |
| /// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol) | |
| /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. | |
| library SafeTransferLib { | |
| /*/////////////////////////////////////////////////////////////// | |
| ETH OPERATIONS | |
| //////////////////////////////////////////////////////////////*/ | |
| function safeTransferETH(address to, uint256 amount) internal { | |
| bool callStatus; | |
| assembly { | |
| // Transfer the ETH and store if it succeeded or not. | |
| callStatus := call(gas(), to, amount, 0, 0, 0, 0) | |
| } | |
| require(callStatus, "ETH_TRANSFER_FAILED"); | |
| } | |
| /*/////////////////////////////////////////////////////////////// | |
| ERC20 OPERATIONS | |
| //////////////////////////////////////////////////////////////*/ | |
| function safeTransferFrom( | |
| address token, | |
| address from, | |
| address to, | |
| uint256 amount | |
| ) internal { | |
| bool callStatus; | |
| assembly { | |
| // Get a pointer to some free memory. | |
| let freeMemoryPointer := mload(0x40) | |
| // Write the abi-encoded calldata to memory piece by piece: | |
| mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector. | |
| mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument. | |
| mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. | |
| mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. | |
| // Call the token and store if it succeeded or not. | |
| // We use 100 because the calldata length is 4 + 32 * 3. | |
| callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0) | |
| } | |
| require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED"); | |
| } | |
| function safeTransfer( | |
| address token, | |
| address to, | |
| uint256 amount | |
| ) internal { | |
| bool callStatus; | |
| assembly { | |
| // Get a pointer to some free memory. | |
| let freeMemoryPointer := mload(0x40) | |
| // Write the abi-encoded calldata to memory piece by piece: | |
| mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector. | |
| mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. | |
| mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. | |
| // Call the token and store if it succeeded or not. | |
| // We use 68 because the calldata length is 4 + 32 * 2. | |
| callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) | |
| } | |
| require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED"); | |
| } | |
| function safeApprove( | |
| address token, | |
| address to, | |
| uint256 amount | |
| ) internal { | |
| bool callStatus; | |
| assembly { | |
| // Get a pointer to some free memory. | |
| let freeMemoryPointer := mload(0x40) | |
| // Write the abi-encoded calldata to memory piece by piece: | |
| mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector. | |
| mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument. | |
| mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value. | |
| // Call the token and store if it succeeded or not. | |
| // We use 68 because the calldata length is 4 + 32 * 2. | |
| callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0) | |
| } | |
| require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED"); | |
| } | |
| /*/////////////////////////////////////////////////////////////// | |
| INTERNAL HELPER LOGIC | |
| //////////////////////////////////////////////////////////////*/ | |
| function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) { | |
| assembly { | |
| // Get how many bytes the call returned. | |
| let returnDataSize := returndatasize() | |
| // If the call reverted: | |
| if iszero(callStatus) { | |
| // Copy the revert message into memory. | |
| returndatacopy(0, 0, returnDataSize) | |
| // Revert with the same message. | |
| revert(0, returnDataSize) | |
| } | |
| switch returnDataSize | |
| case 32 { | |
| // Copy the return data into memory. | |
| returndatacopy(0, 0, returnDataSize) | |
| // Set success to whether it returned true. | |
| success := iszero(iszero(mload(0))) | |
| } | |
| case 0 { | |
| // There was no return data. | |
| success := 1 | |
| } | |
| default { | |
| // It returned some malformed input. | |
| success := 0 | |
| } | |
| } | |
| } | |
| } | |
| contract Vesting { | |
| using SafeTransferLib for address; | |
| uint256 vestingCount; | |
| mapping(uint256 => Vest) public vestings; | |
| struct Vest { | |
| address account; | |
| address asset; | |
| uint256 amount; | |
| uint256 deadline; | |
| } | |
| constructor(address[] memory account, address[] memory asset, uint256[] memory amount, uint256[] memory deadline) payable { | |
| unchecked { | |
| for (uint256 i; i < account.length; i++) { | |
| vestingCount++; | |
| uint256 vestingId = vestingCount; | |
| vestings[vestingId] = Vest(account[i], asset[i], amount[i], deadline[i]); | |
| } | |
| } | |
| } | |
| function claim(uint256 id) external { | |
| require(msg.sender == vestings[id].account, 'NOT_ACCOUNT'); | |
| require(vestings[id].deadline <= block.timestamp, 'NOT_TIME'); | |
| vestings[id].asset.safeTransfer(msg.sender, vestings[id].amount); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment