Skip to content

Instantly share code, notes, and snippets.

@Cr0wn-Gh0ul
Last active November 17, 2022 22:08
Show Gist options
  • Save Cr0wn-Gh0ul/6dabbdb9d542943819f907dbc1e17941 to your computer and use it in GitHub Desktop.
Save Cr0wn-Gh0ul/6dabbdb9d542943819f907dbc1e17941 to your computer and use it in GitHub Desktop.
//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