// SPDX-License-Identifier: UNLICENSEDpragmasolidity 0.8.25;import {EmCore} from"./EmCore.sol";
import {EmToken} from"./EmToken.sol";
errorNotFoundry();
/// @title The Em protocol user activity bookkeeper./// @notice Since this version of the protocol is not deployed on gas-expensive networks, this contract is designed to make data more available from onchain.contractEmLedger{
structStats {
uint256 totalVolume;
uint256 totalLiquidityBootstrapped;
uint256 totalTokensCreated;
uint256 totalTokensGraduated;
uint256 totalTrades;
}
structTrade {
EmToken token;
bool isBuy;
address maker;
uint256 amountIn;
uint256 amountOut;
uint128 timestamp;
uint128 blockNumber;
}
uint256public totalVolume;
uint256public totalLiquidityBootstrapped;
mapping(address=> EmToken[]) public tokensCreatedBy;
mapping(address=> EmToken[]) public tokensTradedBy;
mapping(EmToken =>mapping(address=>bool)) public hasTraded;
EmToken[] public tokensCreated;
EmToken[] public tokensGraduated;
mapping(EmToken =>bool) public isGraduated;
Trade[] public trades;
mapping(EmToken =>uint256[]) public tradesByToken;
mapping(address=>uint256[]) public tradesByUser;
EmCore publicimmutable emCore;
constructor() {
emCore = EmCore(msg.sender);
}
modifieronlyFoundry() {
if (msg.sender!=address(emCore)) revert NotFoundry();
_;
}
/// Add a token to the list of tokens created by a user/// @param token The token to add/// @param user The user to add the token for/// @notice This method should only be called once per token creationfunctionaddCreation(EmToken token, address user) publiconlyFoundry{
tokensCreatedBy[user].push(token);
tokensCreated.push(token);
}
/// Add a trade to the ledger/// @param trade The trade to addfunctionaddTrade(Trade memory trade) publiconlyFoundry{
uint256 tradeId = trades.length;
trades.push(trade);
tradesByToken[trade.token].push(tradeId);
tradesByUser[trade.maker].push(tradeId);
totalVolume += trade.isBuy ? trade.amountIn : trade.amountOut;
if (hasTraded[trade.token][trade.maker]) return;
tokensTradedBy[trade.maker].push(trade.token);
hasTraded[trade.token][trade.maker] =true;
}
/// Add a token to the list of graduated tokens/// @param token The token to add/// @notice This method should only be called once per token graduationfunctionaddGraduation(
EmToken token,
uint256 amountEth
) publiconlyFoundry{
tokensGraduated.push(token);
isGraduated[token] =true;
totalLiquidityBootstrapped += amountEth;
}
/*///////////////////////////////////////////
// Storage Getters //
///////////////////////////////////////////*/functiongetTokensCreatedBy(address user,
uint256 offset,
uint256 limit
) publicviewreturns (EmToken[] memory) {
EmToken[] storage allTokens = tokensCreatedBy[user];
uint256 length = allTokens.length;
if (offset >= length) {
returnnew EmToken[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
EmToken[] memory result =new EmToken[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = allTokens[i];
}
return result;
}
functiongetTokensTradedBy(address user,
uint256 offset,
uint256 limit
) publicviewreturns (EmToken[] memory) {
EmToken[] storage allTokens = tokensTradedBy[user];
uint256 length = allTokens.length;
if (offset >= length) {
returnnew EmToken[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
EmToken[] memory result =new EmToken[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = allTokens[i];
}
return result;
}
functiongetTokens(uint256 offset,
uint256 limit
) publicviewreturns (EmToken[] memory) {
uint256 length = tokensCreated.length;
if (offset >= length) {
returnnew EmToken[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
EmToken[] memory result =new EmToken[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = tokensCreated[i];
}
return result;
}
functiongetToken(uint256 tokenId) publicviewreturns (EmToken) {
return tokensCreated[tokenId];
}
functiongetTokensLength() publicviewreturns (uint256) {
return tokensCreated.length;
}
functiongetTokensGraduated(uint256 offset,
uint256 limit
) publicviewreturns (EmToken[] memory) {
uint256 length = tokensGraduated.length;
if (offset >= length) {
returnnew EmToken[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
EmToken[] memory result =new EmToken[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = tokensGraduated[i];
}
return result;
}
functiongetTokenGraduated(uint256 tokenId) publicviewreturns (EmToken) {
return tokensGraduated[tokenId];
}
functiongetTokensGraduatedLength() publicviewreturns (uint256) {
return tokensGraduated.length;
}
functiongetTradesAll(uint256 offset,
uint256 limit
) publicviewreturns (Trade[] memory) {
uint256 length = trades.length;
if (offset >= length) {
returnnew Trade[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
Trade[] memory result =new Trade[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = trades[i];
}
return result;
}
functiongetTrade(uint256 tradeId) publicviewreturns (Trade memory) {
return trades[tradeId];
}
functiongetTradesLength() publicviewreturns (uint256) {
return trades.length;
}
functiongetTradesByTokenLength(
EmToken token
) publicviewreturns (uint256) {
return tradesByToken[token].length;
}
functiongetTradeIdsByToken(
EmToken token,
uint256 offset,
uint256 limit
) publicviewreturns (uint256[] memory) {
uint256 length = tradesByToken[token].length;
if (offset >= length) {
returnnewuint256[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
uint256[] memory result =newuint256[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = tradesByToken[token][i];
}
return result;
}
functiongetTradesByUserLength(address user) publicviewreturns (uint256) {
return tradesByUser[user].length;
}
functiongetTradeIdsByUser(address user,
uint256 offset,
uint256 limit
) publicviewreturns (uint256[] memory) {
uint256 length = tradesByUser[user].length;
if (offset >= length) {
returnnewuint256[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
uint256[] memory result =newuint256[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = tradesByUser[user][i];
}
return result;
}
functiongetStats() publicviewreturns (Stats memory) {
return
Stats({
totalVolume: totalVolume,
totalLiquidityBootstrapped: totalLiquidityBootstrapped,
totalTokensCreated: tokensCreated.length,
totalTokensGraduated: tokensGraduated.length,
totalTrades: trades.length
});
}
}
Contract Source Code
File 5 of 11: EmToken.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity 0.8.25;import {ERC20} from"solmate/tokens/ERC20.sol";
import {EmCore} from"./EmCore.sol";
errorNotEmCore();
errorForbidden();
/// @title The Em protocol ERC20 token template./// @notice Until graduation, the token allowance is restricted to only the EmCore, and transfers to certain external entities are not/// allowed (eg. Uniswap pairs). This makes sure the token is transferable but not tradable before graduation.contractEmTokenisERC20{
structMetadata {
EmToken token;
string name;
string symbol;
string description;
string extended;
address creator;
bool isGraduated;
uint256 mcap;
}
stringpublic description;
stringpublic extended;
EmCore publicimmutable emCore;
addresspublicimmutable creator;
address[] public holders;
mapping(address=>bool) public isHolder;
/// @notice Locked before graduation to restrict trading to EmCoreboolpublic isUnrestricted =false;
constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals,
uint256 _supply,
stringmemory _description,
stringmemory _extended,
address _emCore,
address _creator
) ERC20(_name, _symbol, _decimals) {
description = _description;
extended = _extended;
emCore = EmCore(_emCore);
creator = _creator;
_mint(msg.sender, _supply);
_addHolder(msg.sender);
}
function_addHolder(address holder) private{
if (!isHolder[holder]) {
holders.push(holder);
isHolder[holder] =true;
}
}
functiongetMetadata() publicviewreturns (Metadata memory) {
EmCore.Pool memory pool = emCore.getPool(this);
return
Metadata(
EmToken(address(this)),
this.name(),
this.symbol(),
description,
extended,
creator,
isGraduated(),
pool.lastMcapInEth
);
}
functionisGraduated() publicviewreturns (bool) {
EmCore.Pool memory pool = emCore.getPool(this);
return pool.headmaster !=address(0);
}
functionsetIsUnrestricted(bool _isUnrestricted) public{
if (msg.sender!=address(emCore)) revert NotEmCore();
isUnrestricted = _isUnrestricted;
}
functiontransfer(address to,
uint256 amount
) publicoverridereturns (bool) {
if (!isUnrestricted) {
bool isPregradRestricted = emCore
.externalEntities_()
.isPregradRestricted(address(this), address(to));
if (isPregradRestricted) revert Forbidden();
}
_addHolder(to);
returnsuper.transfer(to, amount);
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicoverridereturns (bool) {
if (!isUnrestricted) {
bool isPregradRestricted = emCore
.externalEntities_()
.isPregradRestricted(address(this), address(to));
if (isPregradRestricted) revert Forbidden();
}
// Pre-approve EmCore for improved UXif (allowance[from][address(emCore)] !=type(uint256).max) {
allowance[from][address(emCore)] =type(uint256).max;
}
_addHolder(to);
returnsuper.transferFrom(from, to, amount);
}
functionapprove(address spender,
uint256 amount
) publicoverridereturns (bool) {
if (!isUnrestricted) revert Forbidden();
returnsuper.approve(spender, amount);
}
functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicoverride{
if (!isUnrestricted) revert Forbidden();
super.permit(owner, spender, value, deadline, v, r, s);
}
/// Get all addresses who have ever held the token with their balances/// @return The holders and their balances/// @notice Some holders may have a zero balancefunctiongetHoldersWithBalance(uint256 offset,
uint256 limit
) publicviewreturns (address[] memory, uint256[] memory) {
uint256 length = holders.length;
if (offset >= length) {
return (newaddress[](0), newuint256[](0));
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
address[] memory resultAddresses =newaddress[](end - offset);
uint256[] memory resultBalances =newuint256[](end - offset);
for (uint256 i = offset; i < end; i++) {
address holder = holders[i];
resultAddresses[i - offset] = holder;
resultBalances[i - offset] = balanceOf[holder];
}
return (resultAddresses, resultBalances);
}
/// Get all addresses who have ever held the token/// @return The holders/// @notice Some holders may have a zero balancefunctiongetHolders(uint256 offset,
uint256 limit
) publicviewreturns (address[] memory) {
uint256 length = holders.length;
if (offset >= length) {
returnnewaddress[](0);
}
uint256 end = offset + limit;
if (end > length) {
end = length;
}
address[] memory result =newaddress[](end - offset);
for (uint256 i = offset; i < end; i++) {
result[i - offset] = holders[i];
}
return result;
}
/// Get the number of all addresses who have ever held the token/// @return The number of holders/// @notice Some holders may have a zero balancefunctiongetHoldersLength() publicviewreturns (uint256) {
return holders.length;
}
}
Contract Source Code
File 6 of 11: ExternalEntities.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity 0.8.25;import {ERC20} from"solmate/tokens/ERC20.sol";
import {EmCore} from"./EmCore.sol";
import {IUniswapV2Factory} from"./interfaces/IUniswapV2Factory.sol";
errorForbidden();
/// @title External entities registry. Primarily used to check and restrict pre-graduation token transfers to specific entities like Uniswap V2 pairs./// @notice Refer to the EmToken template contract to verify that the restriction is lifted after graduation.contractExternalEntities{
addresspublicimmutable weth;
IUniswapV2Factory[] public knownFactories;
mapping(address=>bool) public pregradRestricted;
addresspublic owner;
constructor(address _weth) {
owner =msg.sender;
weth = _weth;
}
functionsetOwner(address _owner) external{
if (msg.sender!= owner) revert Forbidden();
owner = _owner;
}
functionaddFactory(address factory) external{
if (msg.sender!= owner) revert Forbidden();
knownFactories.push(IUniswapV2Factory(factory));
}
functionremoveFactory(address factory) external{
if (msg.sender!= owner) revert Forbidden();
for (uint256 i =0; i < knownFactories.length; i++) {
if (address(knownFactories[i]) == factory) {
knownFactories[i] = knownFactories[knownFactories.length-1];
knownFactories.pop();
break;
}
}
}
functionaddPregradRestricted(address to) external{
if (msg.sender!= owner) revert Forbidden();
pregradRestricted[to] =true;
}
functionremovePregradRestricted(address to) external{
if (msg.sender!= owner) revert Forbidden();
pregradRestricted[to] =false;
}
functioncomputeUniV2Pair(
IUniswapV2Factory factory,
address tokenA,
address tokenB
) publicviewreturns (address pair, bool exists) {
pair = factory.getPair(tokenA, tokenB);
if (pair !=address(0)) {
return (pair, true);
}
(address token0, address token1) = tokenA < tokenB
? (tokenA, tokenB)
: (tokenB, tokenA);
// both uniswap and quickswap v2 are using the same init code hash
pair =address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory,
keccak256(abi.encodePacked(token0, token1)),
hex"96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f"
)
)
)
)
);
return (pair, false);
}
functionisPregradRestricted(address token,
address to
) externalviewreturns (bool) {
for (uint256 i =0; i < knownFactories.length; i++) {
(address pair, ) = computeUniV2Pair(knownFactories[i], token, weth);
if (pair == to) {
returntrue;
}
}
return pregradRestricted[to];
}
}
Contract Source Code
File 7 of 11: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Arithmetic library with operations for fixed-point numbers./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant MAX_UINT256 =2**256-1;
uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z :=div(mul(x, y), denominator)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,// 1 is added to round up the division of x * y by the denominator.
z :=add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.let half :=shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n :=shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n :=shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.// Equivalent to iszero(eq(div(xx, x), x)) here.ifshr(128, x) {
revert(0, 0)
}
// Store x squared.let xx :=mul(x, x)
// Round to the nearest number.let xxRound :=add(xx, half)
// Revert if xx + half overflowed.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
let y := x // We start y at x, which will help us make our initial estimate.
z :=181// The "correct" value is 1, but this saves a multiplication later.// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.// We check y >= 2^(k + 8) but shift right by k bits// each branch to ensure that if x >= 256, then y >= 256.ifiszero(lt(y, 0x10000000000000000000000000000000000)) {
y :=shr(128, y)
z :=shl(64, z)
}
ifiszero(lt(y, 0x1000000000000000000)) {
y :=shr(64, y)
z :=shl(32, z)
}
ifiszero(lt(y, 0x10000000000)) {
y :=shr(32, y)
z :=shl(16, z)
}
ifiszero(lt(y, 0x1000000)) {
y :=shr(16, y)
z :=shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could// get y in a tighter range. Currently, we will have y in [256, 256*2^16).// We ensured y >= 256 so that the relative difference between y and y+1 is small.// That's not possible if x < 256 but we can just verify those cases exhaustively.// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.// There is no overflow risk here since y < 2^136 after the first branch above.
z :=shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z :=sub(z, lt(div(x, z), z))
}
}
functionunsafeMod(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Mod x by y. Note this will return// 0 instead of reverting if y is zero.
z :=mod(x, y)
}
}
functionunsafeDiv(uint256 x, uint256 y) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
// Divide x by y. Note this will return// 0 instead of reverting if y is zero.
r :=div(x, y)
}
}
functionunsafeDivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Add 1 to x * y if x % y > 0. Note this will// return 0 instead of reverting if y is zero.
z :=add(gt(mod(x, y), 0), div(x, y))
}
}
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
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())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
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())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
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())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}