Optimized SafeTransferLib (Solady)
This commit is contained in:
parent
9d14f053d5
commit
8ebef3aaf1
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../build-info/e4fd58ee172659ae7bbfbfbb00ad761b.json"
|
"buildInfo": "../../../../build-info/cf6d26ad613ca851d9115e4f052bd17c.json"
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,23 @@
|
|||||||
"_format": "hh-sol-artifact-1",
|
"_format": "hh-sol-artifact-1",
|
||||||
"contractName": "SafeTransferLib",
|
"contractName": "SafeTransferLib",
|
||||||
"sourceName": "contracts/lib/utils/SafeTransferLib.sol",
|
"sourceName": "contracts/lib/utils/SafeTransferLib.sol",
|
||||||
"abi": [],
|
"abi": [
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "ETHTransferFailed",
|
||||||
|
"type": "error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "TransferFailed",
|
||||||
|
"type": "error"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "TransferFromFailed",
|
||||||
|
"type": "error"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a",
|
"bytecode": "0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a",
|
||||||
"deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a",
|
"deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a",
|
||||||
"linkReferences": {},
|
"linkReferences": {},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../build-info/577d08e3f9a8a685ac15a7ca46572ea5.json"
|
"buildInfo": "../../build-info/cf6d26ad613ca851d9115e4f052bd17c.json"
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -4,114 +4,140 @@ pragma solidity >=0.8.4;
|
|||||||
import { ERC20 } from "../tokens/ERC20.sol";
|
import { ERC20 } from "../tokens/ERC20.sol";
|
||||||
|
|
||||||
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
|
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
|
||||||
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
|
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
|
||||||
|
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
|
||||||
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
|
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
|
||||||
library SafeTransferLib {
|
library SafeTransferLib {
|
||||||
|
/*//////////////////////////////////////////////////////////////
|
||||||
|
CUSTOM ERRORS
|
||||||
|
//////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
/// @dev The ETH transfer has failed.
|
||||||
|
error ETHTransferFailed();
|
||||||
|
/// @dev The ERC20 `transferFrom` has failed.
|
||||||
|
error TransferFromFailed();
|
||||||
|
/// @dev The ERC20 `transfer` has failed.
|
||||||
|
error TransferFailed();
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////
|
||||||
ETH OPERATIONS
|
ETH OPERATIONS
|
||||||
//////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
/// @dev Sends `amount` (in wei) ETH to `to`.
|
||||||
|
/// Reverts upon failure.
|
||||||
function safeTransferETH(
|
function safeTransferETH(
|
||||||
address to,
|
address to,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
) internal {
|
) internal {
|
||||||
bool success;
|
/// @solidity memory-safe-assembly
|
||||||
|
|
||||||
assembly {
|
assembly {
|
||||||
// Transfer the ETH and store if it succeeded or not.
|
// Transfer the ETH and check if it succeeded or not.
|
||||||
success := call(gas(), to, amount, 0, 0, 0, 0)
|
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
|
||||||
|
// Store the function selector of `ETHTransferFailed()`.
|
||||||
|
mstore(0x00, 0xb12d13eb)
|
||||||
|
// Revert with (offset, size).
|
||||||
|
revert(0x1c, 0x04)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require(success, "ETH_TRANSFER_FAILED");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////
|
||||||
ERC20 OPERATIONS
|
ERC20 OPERATIONS
|
||||||
//////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
|
||||||
|
/// Reverts upon failure.
|
||||||
|
///
|
||||||
|
/// The `from` account must have at least `amount` approved for
|
||||||
|
/// the current contract to manage.
|
||||||
function safeTransferFrom(
|
function safeTransferFrom(
|
||||||
ERC20 token,
|
ERC20 token,
|
||||||
address from,
|
address from,
|
||||||
address to,
|
address to,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
) internal {
|
) internal {
|
||||||
bool success;
|
/// @solidity memory-safe-assembly
|
||||||
|
|
||||||
assembly {
|
assembly {
|
||||||
// We'll write our calldata to this slot below, but restore it later.
|
let m := mload(0x40) // Cache the free memory pointer.
|
||||||
let memPointer := mload(0x40)
|
|
||||||
|
|
||||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
mstore(0x60, amount) // Store the `amount` argument.
|
||||||
mstore(
|
mstore(0x40, to) // Store the `to` argument.
|
||||||
0,
|
mstore(0x2c, shl(96, from)) // Store the `from` argument.
|
||||||
0x23b872dd00000000000000000000000000000000000000000000000000000000
|
// Store the function selector of `transferFrom(address,address,uint256)`.
|
||||||
)
|
mstore(0x0c, 0x23b872dd000000000000000000000000)
|
||||||
mstore(4, from) // Append the "from" argument.
|
|
||||||
mstore(36, to) // Append the "to" argument.
|
|
||||||
mstore(68, amount) // Append the "amount" argument.
|
|
||||||
|
|
||||||
success := and(
|
if iszero(
|
||||||
// Set success to whether the call reverted, if not we check it either
|
and(
|
||||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
// The arguments of `and` are evaluated from right to left.
|
||||||
or(
|
// Set success to whether the call reverted, if not we check it either
|
||||||
and(
|
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||||
eq(mload(0), 1),
|
or(
|
||||||
gt(returndatasize(), 31)
|
eq(mload(0x00), 1),
|
||||||
|
iszero(returndatasize())
|
||||||
),
|
),
|
||||||
iszero(returndatasize())
|
call(
|
||||||
),
|
gas(),
|
||||||
// We use 100 because that's the total length of our calldata (4 + 32 * 3)
|
token,
|
||||||
// Counterintuitively, this call() must be positioned after the or() in the
|
0,
|
||||||
// surrounding and() because and() evaluates its arguments from right to left.
|
0x1c,
|
||||||
call(gas(), token, 0, 0, 100, 0, 32)
|
0x64,
|
||||||
)
|
0x00,
|
||||||
|
0x20
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// Store the function selector of `TransferFromFailed()`.
|
||||||
|
mstore(0x00, 0x7939f424)
|
||||||
|
// Revert with (offset, size).
|
||||||
|
revert(0x1c, 0x04)
|
||||||
|
}
|
||||||
|
|
||||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||||
mstore(0x40, memPointer) // Restore the memPointer.
|
mstore(0x40, m) // Restore the free memory pointer.
|
||||||
}
|
}
|
||||||
|
|
||||||
require(success, "TRANSFER_FROM_FAILED");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
|
||||||
|
/// Reverts upon failure.
|
||||||
function safeTransfer(
|
function safeTransfer(
|
||||||
ERC20 token,
|
ERC20 token,
|
||||||
address to,
|
address to,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
) internal {
|
) internal {
|
||||||
bool success;
|
/// @solidity memory-safe-assembly
|
||||||
|
|
||||||
assembly {
|
assembly {
|
||||||
// We'll write our calldata to this slot below, but restore it later.
|
mstore(0x14, to) // Store the `to` argument.
|
||||||
let memPointer := mload(0x40)
|
mstore(0x34, amount) // Store the `amount` argument.
|
||||||
|
// Store the function selector of `transfer(address,uint256)`.
|
||||||
|
mstore(0x00, 0xa9059cbb000000000000000000000000)
|
||||||
|
|
||||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
if iszero(
|
||||||
mstore(
|
and(
|
||||||
0,
|
// The arguments of `and` are evaluated from right to left.
|
||||||
0xa9059cbb00000000000000000000000000000000000000000000000000000000
|
// Set success to whether the call reverted, if not we check it either
|
||||||
)
|
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
||||||
mstore(4, to) // Append the "to" argument.
|
or(
|
||||||
mstore(36, amount) // Append the "amount" argument.
|
eq(mload(0x00), 1),
|
||||||
|
iszero(returndatasize())
|
||||||
success := and(
|
|
||||||
// Set success to whether the call reverted, if not we check it either
|
|
||||||
// returned exactly 1 (can't just be non-zero data), or had no return data.
|
|
||||||
or(
|
|
||||||
and(
|
|
||||||
eq(mload(0), 1),
|
|
||||||
gt(returndatasize(), 31)
|
|
||||||
),
|
),
|
||||||
iszero(returndatasize())
|
call(
|
||||||
),
|
gas(),
|
||||||
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
|
token,
|
||||||
// Counterintuitively, this call() must be positioned after the or() in the
|
0,
|
||||||
// surrounding and() because and() evaluates its arguments from right to left.
|
0x10,
|
||||||
call(gas(), token, 0, 0, 68, 0, 32)
|
0x44,
|
||||||
)
|
0x00,
|
||||||
|
0x20
|
||||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
)
|
||||||
mstore(0x40, memPointer) // Restore the memPointer.
|
)
|
||||||
|
) {
|
||||||
|
// Store the function selector of `TransferFailed()`.
|
||||||
|
mstore(0x00, 0x90b8ec18)
|
||||||
|
// Revert with (offset, size).
|
||||||
|
revert(0x1c, 0x04)
|
||||||
|
}
|
||||||
|
// Restore the part of the free memory pointer that was overwritten.
|
||||||
|
mstore(0x34, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
require(success, "TRANSFER_FAILED");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
src/types/factories/lib/utils/SafeTransferLib__factory.ts
Normal file
78
src/types/factories/lib/utils/SafeTransferLib__factory.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* Autogenerated file. Do not edit manually. */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import { Signer, utils, Contract, ContractFactory, Overrides } from "ethers";
|
||||||
|
import type { Provider, TransactionRequest } from "@ethersproject/providers";
|
||||||
|
import type { PromiseOrValue } from "../../../common";
|
||||||
|
import type {
|
||||||
|
SafeTransferLib,
|
||||||
|
SafeTransferLibInterface,
|
||||||
|
} from "../../../lib/utils/SafeTransferLib";
|
||||||
|
|
||||||
|
const _abi = [
|
||||||
|
{
|
||||||
|
inputs: [],
|
||||||
|
name: "ETHTransferFailed",
|
||||||
|
type: "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [],
|
||||||
|
name: "TransferFailed",
|
||||||
|
type: "error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
inputs: [],
|
||||||
|
name: "TransferFromFailed",
|
||||||
|
type: "error",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const _bytecode =
|
||||||
|
"0x602d6037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a";
|
||||||
|
|
||||||
|
type SafeTransferLibConstructorParams =
|
||||||
|
| [signer?: Signer]
|
||||||
|
| ConstructorParameters<typeof ContractFactory>;
|
||||||
|
|
||||||
|
const isSuperArgs = (
|
||||||
|
xs: SafeTransferLibConstructorParams
|
||||||
|
): xs is ConstructorParameters<typeof ContractFactory> => xs.length > 1;
|
||||||
|
|
||||||
|
export class SafeTransferLib__factory extends ContractFactory {
|
||||||
|
constructor(...args: SafeTransferLibConstructorParams) {
|
||||||
|
if (isSuperArgs(args)) {
|
||||||
|
super(...args);
|
||||||
|
} else {
|
||||||
|
super(_abi, _bytecode, args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override deploy(
|
||||||
|
overrides?: Overrides & { from?: PromiseOrValue<string> }
|
||||||
|
): Promise<SafeTransferLib> {
|
||||||
|
return super.deploy(overrides || {}) as Promise<SafeTransferLib>;
|
||||||
|
}
|
||||||
|
override getDeployTransaction(
|
||||||
|
overrides?: Overrides & { from?: PromiseOrValue<string> }
|
||||||
|
): TransactionRequest {
|
||||||
|
return super.getDeployTransaction(overrides || {});
|
||||||
|
}
|
||||||
|
override attach(address: string): SafeTransferLib {
|
||||||
|
return super.attach(address) as SafeTransferLib;
|
||||||
|
}
|
||||||
|
override connect(signer: Signer): SafeTransferLib__factory {
|
||||||
|
return super.connect(signer) as SafeTransferLib__factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
static readonly bytecode = _bytecode;
|
||||||
|
static readonly abi = _abi;
|
||||||
|
static createInterface(): SafeTransferLibInterface {
|
||||||
|
return new utils.Interface(_abi) as SafeTransferLibInterface;
|
||||||
|
}
|
||||||
|
static connect(
|
||||||
|
address: string,
|
||||||
|
signerOrProvider: Signer | Provider
|
||||||
|
): SafeTransferLib {
|
||||||
|
return new Contract(address, _abi, signerOrProvider) as SafeTransferLib;
|
||||||
|
}
|
||||||
|
}
|
@ -3,3 +3,4 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export { Multicall__factory } from "./Multicall__factory";
|
export { Multicall__factory } from "./Multicall__factory";
|
||||||
export { ReentrancyGuard__factory } from "./ReentrancyGuard__factory";
|
export { ReentrancyGuard__factory } from "./ReentrancyGuard__factory";
|
||||||
|
export { SafeTransferLib__factory } from "./SafeTransferLib__factory";
|
||||||
|
File diff suppressed because one or more lines are too long
9
src/types/hardhat.d.ts
vendored
9
src/types/hardhat.d.ts
vendored
@ -40,6 +40,10 @@ declare module "hardhat/types/runtime" {
|
|||||||
name: "ReentrancyGuard",
|
name: "ReentrancyGuard",
|
||||||
signerOrOptions?: ethers.Signer | FactoryOptions
|
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||||
): Promise<Contracts.ReentrancyGuard__factory>;
|
): Promise<Contracts.ReentrancyGuard__factory>;
|
||||||
|
getContractFactory(
|
||||||
|
name: "SafeTransferLib",
|
||||||
|
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||||
|
): Promise<Contracts.SafeTransferLib__factory>;
|
||||||
getContractFactory(
|
getContractFactory(
|
||||||
name: "P2PIX",
|
name: "P2PIX",
|
||||||
signerOrOptions?: ethers.Signer | FactoryOptions
|
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||||
@ -84,6 +88,11 @@ declare module "hardhat/types/runtime" {
|
|||||||
address: string,
|
address: string,
|
||||||
signer?: ethers.Signer
|
signer?: ethers.Signer
|
||||||
): Promise<Contracts.ReentrancyGuard>;
|
): Promise<Contracts.ReentrancyGuard>;
|
||||||
|
getContractAt(
|
||||||
|
name: "SafeTransferLib",
|
||||||
|
address: string,
|
||||||
|
signer?: ethers.Signer
|
||||||
|
): Promise<Contracts.SafeTransferLib>;
|
||||||
getContractAt(
|
getContractAt(
|
||||||
name: "P2PIX",
|
name: "P2PIX",
|
||||||
address: string,
|
address: string,
|
||||||
|
@ -21,6 +21,8 @@ export type { Multicall } from "./lib/utils/Multicall";
|
|||||||
export { Multicall__factory } from "./factories/lib/utils/Multicall__factory";
|
export { Multicall__factory } from "./factories/lib/utils/Multicall__factory";
|
||||||
export type { ReentrancyGuard } from "./lib/utils/ReentrancyGuard";
|
export type { ReentrancyGuard } from "./lib/utils/ReentrancyGuard";
|
||||||
export { ReentrancyGuard__factory } from "./factories/lib/utils/ReentrancyGuard__factory";
|
export { ReentrancyGuard__factory } from "./factories/lib/utils/ReentrancyGuard__factory";
|
||||||
|
export type { SafeTransferLib } from "./lib/utils/SafeTransferLib";
|
||||||
|
export { SafeTransferLib__factory } from "./factories/lib/utils/SafeTransferLib__factory";
|
||||||
export type { P2PIX } from "./p2pix.sol/P2PIX";
|
export type { P2PIX } from "./p2pix.sol/P2PIX";
|
||||||
export { P2PIX__factory } from "./factories/p2pix.sol/P2PIX__factory";
|
export { P2PIX__factory } from "./factories/p2pix.sol/P2PIX__factory";
|
||||||
export { Reputation__factory } from "./factories/Reputation__factory";
|
export { Reputation__factory } from "./factories/Reputation__factory";
|
||||||
|
56
src/types/lib/utils/SafeTransferLib.ts
Normal file
56
src/types/lib/utils/SafeTransferLib.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* Autogenerated file. Do not edit manually. */
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
import type { BaseContract, Signer, utils } from "ethers";
|
||||||
|
|
||||||
|
import type { Listener, Provider } from "@ethersproject/providers";
|
||||||
|
import type {
|
||||||
|
TypedEventFilter,
|
||||||
|
TypedEvent,
|
||||||
|
TypedListener,
|
||||||
|
OnEvent,
|
||||||
|
PromiseOrValue,
|
||||||
|
} from "../../common";
|
||||||
|
|
||||||
|
export interface SafeTransferLibInterface extends utils.Interface {
|
||||||
|
functions: {};
|
||||||
|
|
||||||
|
events: {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SafeTransferLib extends BaseContract {
|
||||||
|
connect(signerOrProvider: Signer | Provider | string): this;
|
||||||
|
attach(addressOrName: string): this;
|
||||||
|
deployed(): Promise<this>;
|
||||||
|
|
||||||
|
interface: SafeTransferLibInterface;
|
||||||
|
|
||||||
|
queryFilter<TEvent extends TypedEvent>(
|
||||||
|
event: TypedEventFilter<TEvent>,
|
||||||
|
fromBlockOrBlockhash?: string | number | undefined,
|
||||||
|
toBlock?: string | number | undefined
|
||||||
|
): Promise<Array<TEvent>>;
|
||||||
|
|
||||||
|
listeners<TEvent extends TypedEvent>(
|
||||||
|
eventFilter?: TypedEventFilter<TEvent>
|
||||||
|
): Array<TypedListener<TEvent>>;
|
||||||
|
listeners(eventName?: string): Array<Listener>;
|
||||||
|
removeAllListeners<TEvent extends TypedEvent>(
|
||||||
|
eventFilter: TypedEventFilter<TEvent>
|
||||||
|
): this;
|
||||||
|
removeAllListeners(eventName?: string): this;
|
||||||
|
off: OnEvent<this>;
|
||||||
|
on: OnEvent<this>;
|
||||||
|
once: OnEvent<this>;
|
||||||
|
removeListener: OnEvent<this>;
|
||||||
|
|
||||||
|
functions: {};
|
||||||
|
|
||||||
|
callStatic: {};
|
||||||
|
|
||||||
|
filters: {};
|
||||||
|
|
||||||
|
estimateGas: {};
|
||||||
|
|
||||||
|
populateTransaction: {};
|
||||||
|
}
|
@ -3,3 +3,4 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
export type { Multicall } from "./Multicall";
|
export type { Multicall } from "./Multicall";
|
||||||
export type { ReentrancyGuard } from "./ReentrancyGuard";
|
export type { ReentrancyGuard } from "./ReentrancyGuard";
|
||||||
|
export type { SafeTransferLib } from "./SafeTransferLib";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user