Optimized SafeTransferLib (Solady)
This commit is contained in:
parent
9d14f053d5
commit
8ebef3aaf1
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../../../build-info/e4fd58ee172659ae7bbfbfbb00ad761b.json"
|
||||
"buildInfo": "../../../../build-info/cf6d26ad613ca851d9115e4f052bd17c.json"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,23 @@
|
|||
"_format": "hh-sol-artifact-1",
|
||||
"contractName": "SafeTransferLib",
|
||||
"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",
|
||||
"deployedBytecode": "0x73000000000000000000000000000000000000000030146080604052600080fdfea164736f6c6343000809000a",
|
||||
"linkReferences": {},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"_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";
|
||||
|
||||
/// @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.
|
||||
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
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
/// @dev Sends `amount` (in wei) ETH to `to`.
|
||||
/// Reverts upon failure.
|
||||
function safeTransferETH(
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
// Transfer the ETH and store if it succeeded or not.
|
||||
success := call(gas(), to, amount, 0, 0, 0, 0)
|
||||
// Transfer the ETH and check if it succeeded or not.
|
||||
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
|
||||
//////////////////////////////////////////////////////////////*/
|
||||
|
||||
/// @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(
|
||||
ERC20 token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
let m := mload(0x40) // Cache the free memory pointer.
|
||||
|
||||
// Write the abi-encoded calldata into memory, beginning with the function selector.
|
||||
mstore(
|
||||
0,
|
||||
0x23b872dd00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, from) // Append the "from" argument.
|
||||
mstore(36, to) // Append the "to" argument.
|
||||
mstore(68, amount) // Append the "amount" argument.
|
||||
mstore(0x60, amount) // Store the `amount` argument.
|
||||
mstore(0x40, to) // Store the `to` argument.
|
||||
mstore(0x2c, shl(96, from)) // Store the `from` argument.
|
||||
// Store the function selector of `transferFrom(address,address,uint256)`.
|
||||
mstore(0x0c, 0x23b872dd000000000000000000000000)
|
||||
|
||||
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)
|
||||
if iszero(
|
||||
and(
|
||||
// The arguments of `and` are evaluated from right to left.
|
||||
// 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(
|
||||
eq(mload(0x00), 1),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 100 because that's the total length of our calldata (4 + 32 * 3)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 100, 0, 32)
|
||||
)
|
||||
call(
|
||||
gas(),
|
||||
token,
|
||||
0,
|
||||
0x1c,
|
||||
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(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(
|
||||
ERC20 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal {
|
||||
bool success;
|
||||
|
||||
/// @solidity memory-safe-assembly
|
||||
assembly {
|
||||
// We'll write our calldata to this slot below, but restore it later.
|
||||
let memPointer := mload(0x40)
|
||||
mstore(0x14, to) // Store the `to` argument.
|
||||
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.
|
||||
mstore(
|
||||
0,
|
||||
0xa9059cbb00000000000000000000000000000000000000000000000000000000
|
||||
)
|
||||
mstore(4, to) // Append the "to" argument.
|
||||
mstore(36, amount) // Append the "amount" argument.
|
||||
|
||||
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)
|
||||
if iszero(
|
||||
and(
|
||||
// The arguments of `and` are evaluated from right to left.
|
||||
// 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(
|
||||
eq(mload(0x00), 1),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
iszero(returndatasize())
|
||||
),
|
||||
// We use 68 because that's the total length of our calldata (4 + 32 * 2)
|
||||
// Counterintuitively, this call() must be positioned after the or() in the
|
||||
// surrounding and() because and() evaluates its arguments from right to left.
|
||||
call(gas(), token, 0, 0, 68, 0, 32)
|
||||
)
|
||||
|
||||
mstore(0x60, 0) // Restore the zero slot to zero.
|
||||
mstore(0x40, memPointer) // Restore the memPointer.
|
||||
call(
|
||||
gas(),
|
||||
token,
|
||||
0,
|
||||
0x10,
|
||||
0x44,
|
||||
0x00,
|
||||
0x20
|
||||
)
|
||||
)
|
||||
) {
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
export { Multicall__factory } from "./Multicall__factory";
|
||||
export { ReentrancyGuard__factory } from "./ReentrancyGuard__factory";
|
||||
export { SafeTransferLib__factory } from "./SafeTransferLib__factory";
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -40,6 +40,10 @@ declare module "hardhat/types/runtime" {
|
|||
name: "ReentrancyGuard",
|
||||
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||
): Promise<Contracts.ReentrancyGuard__factory>;
|
||||
getContractFactory(
|
||||
name: "SafeTransferLib",
|
||||
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||
): Promise<Contracts.SafeTransferLib__factory>;
|
||||
getContractFactory(
|
||||
name: "P2PIX",
|
||||
signerOrOptions?: ethers.Signer | FactoryOptions
|
||||
|
@ -84,6 +88,11 @@ declare module "hardhat/types/runtime" {
|
|||
address: string,
|
||||
signer?: ethers.Signer
|
||||
): Promise<Contracts.ReentrancyGuard>;
|
||||
getContractAt(
|
||||
name: "SafeTransferLib",
|
||||
address: string,
|
||||
signer?: ethers.Signer
|
||||
): Promise<Contracts.SafeTransferLib>;
|
||||
getContractAt(
|
||||
name: "P2PIX",
|
||||
address: string,
|
||||
|
|
|
@ -21,6 +21,8 @@ export type { Multicall } from "./lib/utils/Multicall";
|
|||
export { Multicall__factory } from "./factories/lib/utils/Multicall__factory";
|
||||
export type { ReentrancyGuard } from "./lib/utils/ReentrancyGuard";
|
||||
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 { P2PIX__factory } from "./factories/p2pix.sol/P2PIX__factory";
|
||||
export { Reputation__factory } from "./factories/Reputation__factory";
|
||||
|
|
|
@ -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 */
|
||||
export type { Multicall } from "./Multicall";
|
||||
export type { ReentrancyGuard } from "./ReentrancyGuard";
|
||||
export type { SafeTransferLib } from "./SafeTransferLib";
|
||||
|
|
Loading…
Reference in New Issue