Last active
November 17, 2022 22:08
-
-
Save Cr0wn-Gh0ul/6dabbdb9d542943819f907dbc1e17941 to your computer and use it in GitHub Desktop.
This file contains 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: MIT | |
pragma solidity ^0.8.17; | |
contract MultiSend { | |
enum AssetType { | |
ETH, | |
ERC20, | |
ERC721, | |
ERC1155, | |
CALLDATA | |
} | |
struct Asset { | |
AssetType assetType; | |
address tokenAddress; | |
address payable[] to; | |
uint256[] amount; | |
uint256[] id; | |
bytes[] data; | |
} | |
bytes4 constant ERC20_SELECTOR = | |
bytes4(keccak256(bytes("transferFrom(address,address,uint256)"))); | |
bytes4 constant ERC721_SELECTOR = | |
bytes4(keccak256(bytes("safeTransferFrom(address,address,uint256)"))); | |
bytes4 constant ERC1155_SELECTOR = | |
bytes4( | |
keccak256( | |
bytes("safeTransferFrom(address,address,uint256,uint256,bytes)") | |
) | |
); | |
bytes4 constant ERC1155_BATCH_SELECTOR = | |
bytes4( | |
keccak256( | |
bytes( | |
"safeBatchTransferFrom(address,address,uint256[],uint256[],bytes)" | |
) | |
) | |
); | |
constructor() {} | |
function multiSend(Asset[] calldata asset_) public { | |
for (uint256 i = 0; i < asset_.length; i++) { | |
if (AssetType.ETH == asset_[i].assetType) { | |
_sendEther(asset_[i].to, asset_[i].amount); | |
} else if (AssetType.ERC20 == asset_[i].assetType) { | |
_sendERC20( | |
asset_[i].tokenAddress, | |
asset_[i].to, | |
asset_[i].amount | |
); | |
} else if (AssetType.ERC721 == asset_[i].assetType) { | |
_sendERC721(asset_[i].tokenAddress, asset_[i].to, asset_[i].id); | |
} else if (AssetType.ERC1155 == asset_[i].assetType) { | |
_sendERC1155( | |
asset_[i].tokenAddress, | |
asset_[i].to, | |
asset_[i].id, | |
asset_[i].amount | |
); | |
} else if (AssetType.CALLDATA == asset_[i].assetType) { | |
_multiCall(asset_[i].to, asset_[i].amount, asset_[i].data); | |
} | |
} | |
} | |
function sendEther( | |
address payable[] calldata receivers_, | |
uint256[] calldata amounts_ | |
) public payable { | |
_sendEther(receivers_, amounts_); | |
} | |
function sendERC20( | |
address tokenAddress_, | |
address payable[] calldata receivers_, | |
uint256[] calldata amounts_ | |
) public payable { | |
_sendERC20(tokenAddress_, receivers_, amounts_); | |
} | |
function sendERC721( | |
address tokenContract_, | |
address payable[] calldata receivers_, | |
uint256[] calldata tokenIds_ | |
) public { | |
_sendERC721(tokenContract_, receivers_, tokenIds_); | |
} | |
function sendERC1155( | |
address tokenContract_, | |
address payable[] calldata receivers_, | |
uint256[] calldata tokenIds_, | |
uint256[] calldata amounts_ | |
) public { | |
_sendERC1155(tokenContract_, receivers_, tokenIds_, amounts_); | |
} | |
function multiCall( | |
address payable[] calldata to_, | |
uint256[] calldata amounts_, | |
bytes[] calldata data_ | |
) public { | |
_multiCall(to_, amounts_, data_); | |
} | |
function _sendEther( | |
address payable[] calldata receivers_, | |
uint256[] calldata amounts_ | |
) internal { | |
assembly { | |
if iszero(eq(receivers_.length, amounts_.length)) { | |
revert(0, 0) | |
} | |
let totalValue := 0 | |
for { | |
let i := 0 | |
} lt(i, receivers_.length) { | |
i := add(i, 1) | |
} { | |
let amount := calldataload(add(amounts_.offset, mul(i, 0x20))) | |
totalValue := add(totalValue, amount) | |
let success := call( | |
gas(), | |
and( | |
calldataload(add(receivers_.offset, mul(i, 0x20))), | |
0xffffffffffffffffffffffffffffffffffffffff | |
), | |
amount, | |
0, | |
0, | |
0, | |
0 | |
) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
} | |
} | |
function _sendERC20( | |
address tokenContract_, | |
address payable[] calldata receivers_, | |
uint256[] calldata amounts_ | |
) internal { | |
bytes4 SELECTOR = ERC20_SELECTOR; | |
assembly { | |
if iszero(eq(receivers_.length, amounts_.length)) { | |
revert(0, 0) | |
} | |
for { | |
let i := 0 | |
} lt(i, receivers_.length) { | |
i := add(i, 1) | |
} { | |
let receiver := calldataload( | |
add(receivers_.offset, mul(i, 0x20)) | |
) | |
let amount := calldataload(add(amounts_.offset, mul(i, 0x20))) | |
let ptr := mload(0x40) | |
mstore(ptr, SELECTOR) | |
mstore(add(ptr, 0x04), caller()) | |
mstore( | |
add(ptr, 0x24), | |
calldataload(add(receivers_.offset, mul(i, 0x20))) | |
) | |
mstore( | |
add(ptr, 0x44), | |
calldataload(add(amounts_.offset, mul(i, 0x20))) | |
) | |
let success := call(gas(), tokenContract_, 0, ptr, 0x64, 0, 0) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
} | |
} | |
function _sendERC721( | |
address tokenContract_, | |
address payable[] calldata receivers_, | |
uint256[] calldata tokenIds_ | |
) internal { | |
bytes4 SELECTOR = ERC721_SELECTOR; | |
assembly { | |
if iszero(eq(receivers_.length, tokenIds_.length)) { | |
revert(0, 0) | |
} | |
for { | |
let i := 0 | |
} lt(i, receivers_.length) { | |
i := add(i, 1) | |
} { | |
let ptr := mload(0x40) | |
mstore(ptr, SELECTOR) | |
mstore(add(ptr, 0x04), caller()) | |
mstore( | |
add(ptr, 0x24), | |
calldataload(add(receivers_.offset, mul(i, 0x20))) | |
) | |
mstore( | |
add(ptr, 0x44), | |
calldataload(add(tokenIds_.offset, mul(i, 0x20))) | |
) | |
let success := call(gas(), tokenContract_, 0, ptr, 0x64, 0, 0) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
} | |
} | |
function _sendERC1155( | |
address tokenContract_, | |
address payable[] calldata receivers_, | |
uint256[] calldata tokenIds_, | |
uint256[] calldata amounts_ | |
) internal { | |
bytes4 SELECTOR = ERC1155_SELECTOR; | |
assembly { | |
if iszero(eq(receivers_.length, tokenIds_.length)) { | |
revert(0, 0) | |
} | |
for { | |
let i := 0 | |
} lt(i, receivers_.length) { | |
i := add(i, 1) | |
} { | |
let ptr := mload(0x40) | |
mstore(ptr, SELECTOR) | |
mstore(add(ptr, 0x04), caller()) | |
mstore( | |
add(ptr, 0x24), | |
calldataload(add(receivers_.offset, mul(i, 0x20))) | |
) | |
mstore( | |
add(ptr, 0x44), | |
calldataload(add(tokenIds_.offset, mul(i, 0x20))) | |
) | |
mstore( | |
add(ptr, 0x64), | |
calldataload(add(amounts_.offset, mul(i, 0x20))) | |
) | |
mstore(add(ptr, 0x84), 160) | |
mstore(add(ptr, 0xA4), 0) | |
let success := call(gas(), tokenContract_, 0, ptr, 0xC4, 0, 0) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
} | |
} | |
function _sendBatchERC1155( | |
address tokenContract_, | |
address payable receivers_, | |
uint256[] calldata tokenIds_, | |
uint256[] calldata amounts_ | |
) internal { | |
bytes4 SELECTOR = ERC1155_BATCH_SELECTOR; | |
assembly { | |
if iszero(eq(amounts_.length, tokenIds_.length)) { | |
revert(0, 0) | |
} | |
let ptr := mload(0x40) | |
mstore(ptr, SELECTOR) | |
mstore(add(ptr, 0x04), caller()) | |
mstore(add(ptr, 0x24), receivers_) | |
mstore(add(ptr, 0x44), 0xa0) | |
let tokenEnd := add(0xa0, mul(tokenIds_.length, 0x20)) | |
mstore(add(ptr, 0x64), tokenEnd) | |
mstore(add(ptr, 0x84), add(tokenEnd, mul(amounts_.length, 0x20))) | |
mstore(add(ptr, 0xa4), tokenIds_.length) | |
calldatacopy(add(ptr, 0xc4), tokenIds_.offset, mul(tokenIds_.length, 0x20)) | |
let tokenTail := add(add(ptr,0xc4), mul(tokenIds_.length, 0x20)) | |
mstore(tokenTail, amounts_.length) | |
calldatacopy(add(tokenTail, 0x20), amounts_.offset, mul(amounts_.length, 0x20)) | |
let amountsTail := add(add(tokenTail, 0x20), mul(amounts_.length, 0x20)) | |
mstore(amountsTail, 0) | |
let success := call(gas(), tokenContract_, 0, ptr, amountsTail, 0, 0) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
} | |
function _multiCall( | |
address payable[] calldata to_, | |
uint256[] calldata amounts_, | |
bytes[] calldata data_ | |
) internal { | |
for (uint256 i = 0; i < to_.length; ) { | |
bytes memory data = data_[i]; | |
assembly { | |
let success := call( | |
gas(), | |
calldataload(add(to_.offset, mul(i, 0x20))), | |
calldataload(add(amounts_.offset, mul(i, 0x20))), | |
data, | |
mload(data), | |
0, | |
0 | |
) | |
if iszero(success) { | |
returndatacopy(0x00, 0x00, returndatasize()) | |
revert(0x00, returndatasize()) | |
} | |
} | |
unchecked { | |
i++; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment