编译器
0.8.17+commit.8df45f5f
文件 1 的 30:Address.sol
pragma solidity ^0.8.1;
library Address {
function isContract(address account) internal view returns (bool) {
return account.code.length > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
文件 2 的 30:BMath.sol
pragma solidity ^0.8.17;
library BMath {
uint16 internal constant HUNDRED_PERCENT = 10000;
function getPercentage(uint256 value, uint16 percent) internal pure returns (uint256){
if (percent >= HUNDRED_PERCENT){
return value;
}
return value * percent / HUNDRED_PERCENT;
}
function getInvertedPercentage(uint256 value, uint16 percent) internal pure returns (uint256){
if (percent >= HUNDRED_PERCENT){
return value;
}
return value * HUNDRED_PERCENT / percent;
}
}
文件 3 的 30:ERC1155Holder.sol
pragma solidity ^0.8.0;
import "./ERC1155Receiver.sol";
contract ERC1155Holder is ERC1155Receiver {
function onERC1155Received(
address,
address,
uint256,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155Received.selector;
}
function onERC1155BatchReceived(
address,
address,
uint256[] memory,
uint256[] memory,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC1155BatchReceived.selector;
}
}
文件 4 的 30:ERC1155Receiver.sol
pragma solidity ^0.8.0;
import "../IERC1155Receiver.sol";
import "../../../utils/introspection/ERC165.sol";
abstract contract ERC1155Receiver is ERC165, IERC1155Receiver {
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IERC1155Receiver).interfaceId || super.supportsInterface(interfaceId);
}
}
文件 5 的 30:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 6 的 30:ERC721Holder.sol
pragma solidity ^0.8.0;
import "../IERC721Receiver.sol";
contract ERC721Holder is IERC721Receiver {
function onERC721Received(
address,
address,
uint256,
bytes memory
) public virtual override returns (bytes4) {
return this.onERC721Received.selector;
}
}
文件 7 的 30:ExecInfo.sol
pragma solidity ^0.8.17;
library ExecInfo {
struct SolverData {
address balanceRecipient;
uint16 curFillPercent;
}
struct BatchSolverData {
address balanceRecipient;
uint16[] curFillPercents;
bool[] takersPermitsUsage;
bool transferExactAmounts;
}
struct MakerData {
uint256[] increasedBuyAmounts;
uint16 curFillPercent;
}
}
文件 8 的 30:IDaiLikePermit.sol
pragma solidity ^0.8.0;
interface IDaiLikePermit {
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
function getNonce(address user) external view returns (uint256 nonce);
}
文件 9 的 30:IERC1155.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155 is IERC165 {
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
event URI(string value, uint256 indexed id);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}
文件 10 的 30:IERC1155Receiver.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC1155Receiver is IERC165 {
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}
文件 11 的 30:IERC1271.sol
pragma solidity ^0.8.0;
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
文件 12 的 30:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 13 的 30:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
文件 14 的 30:IERC721.sol
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool _approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
文件 15 的 30:IERC721Receiver.sol
pragma solidity ^0.8.0;
interface IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
文件 16 的 30:IJamBalanceManager.sol
pragma solidity ^0.8.17;
import "../libraries/Signature.sol";
interface IJamBalanceManager {
struct TransferData {
address from;
address receiver;
address[] tokens;
uint256[] amounts;
uint256[] nftIds;
bytes tokenTransferTypes;
uint16 fillPercent;
}
struct Indices {
uint64 batchToApproveInd;
uint64 permitSignaturesInd;
uint64 nftsInd;
uint64 batchLen;
}
function transferTokens(
TransferData calldata transferData
) external;
function transferTokensWithPermits(
TransferData calldata transferData,
Signature.TakerPermitsInfo calldata takerPermitsInfo
) external;
}
文件 17 的 30:IJamSettlement.sol
pragma solidity ^0.8.17;
import "../libraries/JamInteraction.sol";
import "../libraries/JamOrder.sol";
import "../libraries/JamHooks.sol";
import "../libraries/Signature.sol";
import "../libraries/ExecInfo.sol";
interface IJamSettlement {
event Settlement(uint256 indexed nonce);
function settle(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
JamInteraction.Data[] calldata interactions,
JamHooks.Def calldata hooks,
ExecInfo.SolverData calldata solverData
) external payable;
function settleWithPermitsSignatures(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
Signature.TakerPermitsInfo calldata takerPermitsInfo,
JamInteraction.Data[] calldata interactions,
JamHooks.Def calldata hooks,
ExecInfo.SolverData calldata solverData
) external payable;
function settleInternal(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
JamHooks.Def calldata hooks,
ExecInfo.MakerData calldata makerData
) external payable;
function settleInternalWithPermitsSignatures(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
Signature.TakerPermitsInfo calldata takerPermitsInfo,
JamHooks.Def calldata hooks,
ExecInfo.MakerData calldata makerData
) external payable;
function settleBatch(
JamOrder.Data[] calldata orders,
Signature.TypedSignature[] calldata signatures,
Signature.TakerPermitsInfo[] calldata takersPermitsInfo,
JamInteraction.Data[] calldata interactions,
JamHooks.Def[] calldata hooks,
ExecInfo.BatchSolverData calldata solverData
) external payable;
}
文件 18 的 30:IPermit2.sol
pragma solidity ^0.8.17;
interface IPermit2 {
struct AllowanceTransferDetails {
address from;
address to;
uint160 amount;
address token;
}
struct PermitDetails {
address token;
uint160 amount;
uint48 expiration;
uint48 nonce;
}
struct PermitBatch {
PermitDetails[] details;
address spender;
uint256 sigDeadline;
}
function allowance(address user, address token, address spender)
external
view
returns (uint160 amount, uint48 expiration, uint48 nonce);
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
}
文件 19 的 30:JamBalanceManager.sol
pragma solidity ^0.8.17;
import "./interfaces/IJamBalanceManager.sol";
import "./interfaces/IPermit2.sol";
import "./interfaces/IDaiLikePermit.sol";
import "./libraries/JamOrder.sol";
import "./libraries/Signature.sol";
import "./libraries/common/SafeCast160.sol";
import "./libraries/common/BMath.sol";
import "./base/JamTransfer.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
contract JamBalanceManager is IJamBalanceManager {
address private immutable operator;
using SafeERC20 for IERC20;
IPermit2 private immutable PERMIT2;
address private immutable DAI_TOKEN;
uint256 private immutable _chainId;
constructor(address _operator, address _permit2, address _daiAddress) {
operator = _operator;
_chainId = block.chainid;
PERMIT2 = IPermit2(_permit2);
DAI_TOKEN = _daiAddress;
}
modifier onlyOperator(address account) {
require(account == operator, "INVALID_CALLER");
_;
}
function transferTokens(
TransferData calldata data
) onlyOperator(msg.sender) external {
IPermit2.AllowanceTransferDetails[] memory batchTransferDetails;
uint nftsInd;
uint batchLen;
for (uint i; i < data.tokens.length; ++i) {
if (data.tokenTransferTypes[i] == Commands.SIMPLE_TRANSFER) {
IERC20(data.tokens[i]).safeTransferFrom(
data.from, data.receiver, BMath.getPercentage(data.amounts[i], data.fillPercent)
);
} else if (data.tokenTransferTypes[i] == Commands.PERMIT2_TRANSFER) {
if (batchLen == 0){
batchTransferDetails = new IPermit2.AllowanceTransferDetails[](data.tokens.length - i);
}
batchTransferDetails[batchLen++] = IPermit2.AllowanceTransferDetails({
from: data.from,
to: data.receiver,
amount: SafeCast160.toUint160(BMath.getPercentage(data.amounts[i], data.fillPercent)),
token: data.tokens[i]
});
continue;
} else if (data.tokenTransferTypes[i] == Commands.NATIVE_TRANSFER) {
require(data.tokens[i] == JamOrder.NATIVE_TOKEN, "INVALID_NATIVE_TOKEN_ADDRESS");
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
if (data.receiver != operator){
JamTransfer(operator).transferNativeFromContract(
data.receiver, BMath.getPercentage(data.amounts[i], data.fillPercent)
);
}
} else if (data.tokenTransferTypes[i] == Commands.NFT_ERC721_TRANSFER) {
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
require(data.amounts[i] == 1, "INVALID_ERC721_AMOUNT");
IERC721(data.tokens[i]).safeTransferFrom(data.from, data.receiver, data.nftIds[nftsInd++]);
} else if (data.tokenTransferTypes[i] == Commands.NFT_ERC1155_TRANSFER) {
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
IERC1155(data.tokens[i]).safeTransferFrom(data.from, data.receiver, data.nftIds[nftsInd++], data.amounts[i], "");
} else {
revert("INVALID_TRANSFER_TYPE");
}
if (batchLen != 0){
assembly {mstore(batchTransferDetails, sub(mload(batchTransferDetails), 1))}
}
}
require(nftsInd == data.nftIds.length, "INVALID_NFT_IDS_LENGTH");
require(batchLen == batchTransferDetails.length, "INVALID_BATCH_PERMIT2_LENGTH");
if (batchLen != 0){
PERMIT2.transferFrom(batchTransferDetails);
}
}
function transferTokensWithPermits(
TransferData calldata data,
Signature.TakerPermitsInfo calldata takerPermitsInfo
) onlyOperator(msg.sender) external {
IPermit2.AllowanceTransferDetails[] memory batchTransferDetails;
IPermit2.PermitDetails[] memory batchToApprove = new IPermit2.PermitDetails[](takerPermitsInfo.noncesPermit2.length);
Indices memory indices = Indices(0, 0, 0, 0);
for (uint i; i < data.tokens.length; ++i) {
if (data.tokenTransferTypes[i] == Commands.SIMPLE_TRANSFER || data.tokenTransferTypes[i] == Commands.CALL_PERMIT_THEN_TRANSFER) {
if (data.tokenTransferTypes[i] == Commands.CALL_PERMIT_THEN_TRANSFER){
permitToken(
data.from, data.tokens[i], takerPermitsInfo.deadline, takerPermitsInfo.permitSignatures[indices.permitSignaturesInd++]
);
}
IERC20(data.tokens[i]).safeTransferFrom(
data.from, data.receiver, BMath.getPercentage(data.amounts[i], data.fillPercent)
);
} else if (data.tokenTransferTypes[i] == Commands.PERMIT2_TRANSFER || data.tokenTransferTypes[i] == Commands.CALL_PERMIT2_THEN_TRANSFER) {
if (data.tokenTransferTypes[i] == Commands.CALL_PERMIT2_THEN_TRANSFER){
batchToApprove[indices.batchToApproveInd] = IPermit2.PermitDetails({
token: data.tokens[i],
amount: type(uint160).max,
expiration: takerPermitsInfo.deadline,
nonce: takerPermitsInfo.noncesPermit2[indices.batchToApproveInd]
});
++indices.batchToApproveInd;
}
if (indices.batchLen == 0){
batchTransferDetails = new IPermit2.AllowanceTransferDetails[](data.tokens.length - i);
}
batchTransferDetails[indices.batchLen++] = IPermit2.AllowanceTransferDetails({
from: data.from,
to: data.receiver,
amount: SafeCast160.toUint160(BMath.getPercentage(data.amounts[i], data.fillPercent)),
token: data.tokens[i]
});
continue;
} else if (data.tokenTransferTypes[i] == Commands.NATIVE_TRANSFER) {
require(data.tokens[i] == JamOrder.NATIVE_TOKEN, "INVALID_NATIVE_TOKEN_ADDRESS");
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
if (data.receiver != operator){
JamTransfer(operator).transferNativeFromContract(
data.receiver, BMath.getPercentage(data.amounts[i], data.fillPercent)
);
}
} else if (data.tokenTransferTypes[i] == Commands.NFT_ERC721_TRANSFER) {
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
require(data.amounts[i] == 1, "INVALID_ERC721_AMOUNT");
IERC721(data.tokens[i]).safeTransferFrom(data.from, data.receiver, data.nftIds[indices.nftsInd++]);
} else if (data.tokenTransferTypes[i] == Commands.NFT_ERC1155_TRANSFER) {
require(data.fillPercent == BMath.HUNDRED_PERCENT, "INVALID_FILL_PERCENT");
IERC1155(data.tokens[i]).safeTransferFrom(data.from, data.receiver, data.nftIds[indices.nftsInd++], data.amounts[i], "");
} else {
revert("INVALID_TRANSFER_TYPE");
}
if (indices.batchLen != 0){
assembly {mstore(batchTransferDetails, sub(mload(batchTransferDetails), 1))}
}
}
require(indices.batchToApproveInd == batchToApprove.length, "INVALID_NUMBER_OF_TOKENS_TO_APPROVE");
require(indices.batchLen == batchTransferDetails.length, "INVALID_BATCH_PERMIT2_LENGTH");
require(indices.permitSignaturesInd == takerPermitsInfo.permitSignatures.length, "INVALID_NUMBER_OF_PERMIT_SIGNATURES");
require(indices.nftsInd == data.nftIds.length, "INVALID_NFT_IDS_LENGTH");
if (batchToApprove.length != 0) {
PERMIT2.permit({
owner: data.from,
permitBatch: IPermit2.PermitBatch({
details: batchToApprove,
spender: address(this),
sigDeadline: takerPermitsInfo.deadline
}),
signature: takerPermitsInfo.signatureBytesPermit2
});
}
if (indices.batchLen != 0){
PERMIT2.transferFrom(batchTransferDetails);
}
}
function permitToken(
address takerAddress, address tokenAddress, uint deadline, bytes calldata permitSignature
) private {
(bytes32 r, bytes32 s, uint8 v) = Signature.getRsv(permitSignature);
if (tokenAddress == DAI_TOKEN){
if (_chainId == 137){
IDaiLikePermit(tokenAddress).permit(
takerAddress, address(this), IDaiLikePermit(tokenAddress).getNonce(takerAddress), deadline, true, v, r, s
);
} else {
IDaiLikePermit(tokenAddress).permit(
takerAddress, address(this), IERC20Permit(tokenAddress).nonces(takerAddress), deadline, true, v, r, s
);
}
} else {
IERC20Permit(tokenAddress).permit(takerAddress, address(this), type(uint).max, deadline, v, r, s);
}
}
}
文件 20 的 30:JamHooks.sol
pragma solidity ^0.8.17;
import "../libraries/JamInteraction.sol";
library JamHooks {
struct Def {
JamInteraction.Data[] beforeSettle;
JamInteraction.Data[] afterSettle;
}
}
文件 21 的 30:JamInteraction.sol
pragma solidity ^0.8.17;
library JamInteraction {
struct Data {
bool result;
address to;
uint256 value;
bytes data;
}
function execute(Data calldata interaction) internal returns (bool result) {
(bool _result,) = payable(interaction.to).call{ value: interaction.value }(interaction.data);
return _result;
}
}
文件 22 的 30:JamOrder.sol
pragma solidity ^0.8.17;
library Commands {
bytes1 internal constant SIMPLE_TRANSFER = 0x00;
bytes1 internal constant PERMIT2_TRANSFER = 0x01;
bytes1 internal constant CALL_PERMIT_THEN_TRANSFER = 0x02;
bytes1 internal constant CALL_PERMIT2_THEN_TRANSFER = 0x03;
bytes1 internal constant NATIVE_TRANSFER = 0x04;
bytes1 internal constant NFT_ERC721_TRANSFER = 0x05;
bytes1 internal constant NFT_ERC1155_TRANSFER = 0x06;
}
library JamOrder {
address internal constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
struct Data {
address taker;
address receiver;
uint256 expiry;
uint256 nonce;
address executor;
uint16 minFillPercent;
bytes32 hooksHash;
address[] sellTokens;
address[] buyTokens;
uint256[] sellAmounts;
uint256[] buyAmounts;
uint256[] sellNFTIds;
uint256[] buyNFTIds;
bytes sellTokenTransfers;
bytes buyTokenTransfers;
}
}
文件 23 的 30:JamSettlement.sol
pragma solidity ^0.8.17;
import "./JamBalanceManager.sol";
import "./base/JamSigning.sol";
import "./base/JamTransfer.sol";
import "./interfaces/IJamBalanceManager.sol";
import "./interfaces/IJamSettlement.sol";
import "./libraries/JamInteraction.sol";
import "./libraries/JamOrder.sol";
import "./libraries/JamHooks.sol";
import "./libraries/ExecInfo.sol";
import "./libraries/common/BMath.sol";
import "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC721/utils/ERC721Holder.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC1155/utils/ERC1155Holder.sol";
contract JamSettlement is IJamSettlement, ReentrancyGuard, JamSigning, JamTransfer, ERC721Holder, ERC1155Holder {
IJamBalanceManager public immutable balanceManager;
constructor(address _permit2, address _daiAddress) {
balanceManager = new JamBalanceManager(address(this), _permit2, _daiAddress);
}
receive() external payable {}
function runInteractions(JamInteraction.Data[] calldata interactions) internal returns (bool result) {
for (uint i; i < interactions.length; ++i) {
require(interactions[i].to != address(balanceManager));
bool execResult = JamInteraction.execute(interactions[i]);
if (!execResult && interactions[i].result) return false;
}
return true;
}
function settle(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
JamInteraction.Data[] calldata interactions,
JamHooks.Def calldata hooks,
ExecInfo.SolverData calldata solverData
) external payable nonReentrant {
validateOrder(order, hooks, signature, solverData.curFillPercent);
require(runInteractions(hooks.beforeSettle), "BEFORE_SETTLE_HOOKS_FAILED");
balanceManager.transferTokens(
IJamBalanceManager.TransferData(
order.taker, solverData.balanceRecipient, order.sellTokens, order.sellAmounts,
order.sellNFTIds, order.sellTokenTransfers, solverData.curFillPercent
)
);
_settle(order, interactions, hooks, solverData.curFillPercent);
}
function settleWithPermitsSignatures(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
Signature.TakerPermitsInfo calldata takerPermitsInfo,
JamInteraction.Data[] calldata interactions,
JamHooks.Def calldata hooks,
ExecInfo.SolverData calldata solverData
) external payable nonReentrant {
validateOrder(order, hooks, signature, solverData.curFillPercent);
require(runInteractions(hooks.beforeSettle), "BEFORE_SETTLE_HOOKS_FAILED");
balanceManager.transferTokensWithPermits(
IJamBalanceManager.TransferData(
order.taker, solverData.balanceRecipient, order.sellTokens, order.sellAmounts,
order.sellNFTIds, order.sellTokenTransfers, solverData.curFillPercent
), takerPermitsInfo
);
_settle(order, interactions, hooks, solverData.curFillPercent);
}
function settleInternal(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
JamHooks.Def calldata hooks,
ExecInfo.MakerData calldata makerData
) external payable nonReentrant {
validateOrder(order, hooks, signature, makerData.curFillPercent);
require(runInteractions(hooks.beforeSettle), "BEFORE_SETTLE_HOOKS_FAILED");
balanceManager.transferTokens(
IJamBalanceManager.TransferData(
order.taker, msg.sender, order.sellTokens, order.sellAmounts,
order.sellNFTIds, order.sellTokenTransfers, makerData.curFillPercent
)
);
_settleInternal(order, hooks, makerData);
}
function settleInternalWithPermitsSignatures(
JamOrder.Data calldata order,
Signature.TypedSignature calldata signature,
Signature.TakerPermitsInfo calldata takerPermitsInfo,
JamHooks.Def calldata hooks,
ExecInfo.MakerData calldata makerData
) external payable nonReentrant {
validateOrder(order, hooks, signature, makerData.curFillPercent);
require(runInteractions(hooks.beforeSettle), "BEFORE_SETTLE_HOOKS_FAILED");
balanceManager.transferTokensWithPermits(
IJamBalanceManager.TransferData(
order.taker, msg.sender, order.sellTokens, order.sellAmounts,
order.sellNFTIds, order.sellTokenTransfers, makerData.curFillPercent
), takerPermitsInfo
);
_settleInternal(order, hooks, makerData);
}
function settleBatch(
JamOrder.Data[] calldata orders,
Signature.TypedSignature[] calldata signatures,
Signature.TakerPermitsInfo[] calldata takersPermitsInfo,
JamInteraction.Data[] calldata interactions,
JamHooks.Def[] calldata hooks,
ExecInfo.BatchSolverData calldata solverData
) external payable nonReentrant {
validateBatchOrders(orders, hooks, signatures, takersPermitsInfo, solverData.takersPermitsUsage, solverData.curFillPercents);
bool isMaxFill = solverData.curFillPercents.length == 0;
bool executeHooks = hooks.length != 0;
uint takersPermitsInd;
for (uint i; i < orders.length; ++i) {
if (executeHooks){
require(runInteractions(hooks[i].beforeSettle), "BEFORE_SETTLE_HOOKS_FAILED");
}
if (solverData.takersPermitsUsage.length != 0 && solverData.takersPermitsUsage[i]){
balanceManager.transferTokensWithPermits(
IJamBalanceManager.TransferData(
orders[i].taker, solverData.balanceRecipient, orders[i].sellTokens, orders[i].sellAmounts,
orders[i].sellNFTIds, orders[i].sellTokenTransfers, isMaxFill ? BMath.HUNDRED_PERCENT : solverData.curFillPercents[i]
), takersPermitsInfo[takersPermitsInd++]
);
} else {
balanceManager.transferTokens(
IJamBalanceManager.TransferData(
orders[i].taker, solverData.balanceRecipient, orders[i].sellTokens, orders[i].sellAmounts,
orders[i].sellNFTIds, orders[i].sellTokenTransfers, isMaxFill ? BMath.HUNDRED_PERCENT : solverData.curFillPercents[i]
)
);
}
}
require(runInteractions(interactions), "INTERACTIONS_FAILED");
for (uint i; i < orders.length; ++i) {
uint256[] memory curBuyAmounts = solverData.transferExactAmounts ?
orders[i].buyAmounts : calculateNewAmounts(i, orders, solverData.curFillPercents);
transferTokensFromContract(
orders[i].buyTokens, curBuyAmounts, orders[i].buyNFTIds, orders[i].buyTokenTransfers,
orders[i].receiver, isMaxFill ? BMath.HUNDRED_PERCENT : solverData.curFillPercents[i], true
);
if (executeHooks){
require(runInteractions(hooks[i].afterSettle), "AFTER_SETTLE_HOOKS_FAILED");
}
emit Settlement(orders[i].nonce);
}
}
function _settle(
JamOrder.Data calldata order,
JamInteraction.Data[] calldata interactions,
JamHooks.Def calldata hooks,
uint16 curFillPercent
) private {
require(runInteractions(interactions), "INTERACTIONS_FAILED");
transferTokensFromContract(
order.buyTokens, order.buyAmounts, order.buyNFTIds, order.buyTokenTransfers, order.receiver, curFillPercent, false
);
if (order.receiver == address(this)){
require(!hasDuplicate(order.buyTokens, order.buyNFTIds, order.buyTokenTransfers), "DUPLICATE_TOKENS");
require(hooks.afterSettle.length > 0, "AFTER_SETTLE_HOOKS_REQUIRED");
for (uint i; i < hooks.afterSettle.length; ++i){
require(hooks.afterSettle[i].result, "POTENTIAL_TOKENS_LOSS");
}
}
require(runInteractions(hooks.afterSettle), "AFTER_SETTLE_HOOKS_FAILED");
emit Settlement(order.nonce);
}
function _settleInternal(
JamOrder.Data calldata order,
JamHooks.Def calldata hooks,
ExecInfo.MakerData calldata makerData
) private {
uint256[] calldata buyAmounts = validateIncreasedAmounts(makerData.increasedBuyAmounts, order.buyAmounts);
balanceManager.transferTokens(
IJamBalanceManager.TransferData(
msg.sender, order.receiver, order.buyTokens, buyAmounts,
order.buyNFTIds, order.buyTokenTransfers, makerData.curFillPercent
)
);
require(runInteractions(hooks.afterSettle), "AFTER_SETTLE_HOOKS_FAILED");
emit Settlement(order.nonce);
}
}
文件 24 的 30:JamSigning.sol
pragma solidity ^0.8.17;
import "../libraries/JamInteraction.sol";
import "../libraries/JamOrder.sol";
import "../libraries/JamHooks.sol";
import "../libraries/Signature.sol";
import "../libraries/common/BMath.sol";
import "lib/openzeppelin-contracts/contracts/interfaces/IERC1271.sol";
abstract contract JamSigning {
mapping(address => mapping(uint256 => uint256)) private standardNonces;
mapping(address => mapping(uint256 => uint256)) private limitOrdersNonces;
uint256 private constant INF_EXPIRY = 9999999999;
bytes32 private constant DOMAIN_NAME = keccak256("JamSettlement");
bytes32 private constant DOMAIN_VERSION = keccak256("1");
bytes4 private constant EIP1271_MAGICVALUE = bytes4(keccak256("isValidSignature(bytes32,bytes)"));
uint256 private constant ETH_SIGN_HASH_PREFIX = 0x19457468657265756d205369676e6564204d6573736167653a0a333200000000;
bytes32 public constant EIP712_DOMAIN_TYPEHASH = keccak256(abi.encodePacked(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
));
bytes32 public constant JAM_ORDER_TYPE_HASH = keccak256(abi.encodePacked(
"JamOrder(address taker,address receiver,uint256 expiry,uint256 nonce,address executor,uint16 minFillPercent,bytes32 hooksHash,address[] sellTokens,address[] buyTokens,uint256[] sellAmounts,uint256[] buyAmounts,uint256[] sellNFTIds,uint256[] buyNFTIds,bytes sellTokenTransfers,bytes buyTokenTransfers)"
));
bytes32 private immutable _CACHED_DOMAIN_SEPARATOR;
uint256 private immutable _CACHED_CHAIN_ID;
constructor(){
_CACHED_CHAIN_ID = block.chainid;
_CACHED_DOMAIN_SEPARATOR = keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, DOMAIN_NAME, DOMAIN_VERSION, block.chainid, address(this))
);
}
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return block.chainid == _CACHED_CHAIN_ID
? _CACHED_DOMAIN_SEPARATOR
: keccak256(
abi.encode(EIP712_DOMAIN_TYPEHASH, DOMAIN_NAME, DOMAIN_VERSION, block.chainid, address(this))
);
}
function hashHooks(JamHooks.Def memory hooks) public pure returns (bytes32) {
if (hooks.afterSettle.length == 0 && hooks.beforeSettle.length == 0){
return bytes32(0);
}
return keccak256(abi.encode(hooks));
}
function hashOrder(JamOrder.Data calldata order, bytes32 hooksHash) public view returns (bytes32) {
bytes32 dataHash = keccak256(
bytes.concat(
abi.encode(
JAM_ORDER_TYPE_HASH,
order.taker,
order.receiver,
order.expiry,
order.nonce,
order.executor,
order.minFillPercent,
hooksHash
),
abi.encode(
keccak256(abi.encodePacked(order.sellTokens)),
keccak256(abi.encodePacked(order.buyTokens)),
keccak256(abi.encodePacked(order.sellAmounts)),
keccak256(abi.encodePacked(order.buyAmounts)),
keccak256(abi.encodePacked(order.sellNFTIds)),
keccak256(abi.encodePacked(order.buyNFTIds)),
keccak256(order.sellTokenTransfers),
keccak256(order.buyTokenTransfers)
)
)
);
return keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
dataHash
)
);
}
function validateSignature(address validationAddress, bytes32 hash, Signature.TypedSignature calldata signature) public view {
if (signature.signatureType == Signature.Type.EIP712) {
(bytes32 r, bytes32 s, uint8 v) = Signature.getRsv(signature.signatureBytes);
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "Invalid signer");
if (signer != validationAddress) {
revert("Invalid EIP712 order signature");
}
} else if (signature.signatureType == Signature.Type.EIP1271) {
require(
IERC1271(validationAddress).isValidSignature(hash, signature.signatureBytes) == EIP1271_MAGICVALUE,
"Invalid EIP1271 order signature"
);
} else if (signature.signatureType == Signature.Type.ETHSIGN) {
bytes32 ethSignHash;
assembly {
mstore(0, ETH_SIGN_HASH_PREFIX)
mstore(28, hash)
ethSignHash := keccak256(0, 60)
}
(bytes32 r, bytes32 s, uint8 v) = Signature.getRsv(signature.signatureBytes);
address signer = ecrecover(ethSignHash, v, r, s);
require(signer != address(0), "Invalid signer");
if (signer != validationAddress) {
revert("Invalid ETHSIGH order signature");
}
} else {
revert("Invalid Signature Type");
}
}
function validateOrder(
JamOrder.Data calldata order, JamHooks.Def memory hooks, Signature.TypedSignature calldata signature, uint16 curFillPercent
) internal {
if (order.taker != msg.sender) {
bytes32 hooksHash = hashHooks(hooks);
bytes32 orderHash = hashOrder(order, hooksHash);
validateSignature(order.taker, orderHash, signature);
}
require(order.executor == msg.sender || order.executor == address(0), "INVALID_EXECUTOR");
require(order.buyTokens.length == order.buyAmounts.length, "INVALID_BUY_TOKENS_LENGTH");
require(order.buyTokens.length == order.buyTokenTransfers.length, "INVALID_BUY_TRANSFERS_LENGTH");
require(order.sellTokens.length == order.sellAmounts.length, "INVALID_SELL_TOKENS_LENGTH");
require(order.sellTokens.length == order.sellTokenTransfers.length, "INVALID_SELL_TRANSFERS_LENGTH");
require(curFillPercent >= order.minFillPercent, "INVALID_FILL_PERCENT");
invalidateOrderNonce(order.taker, order.nonce, order.expiry == INF_EXPIRY);
require(block.timestamp < order.expiry, "ORDER_EXPIRED");
}
function cancelLimitOrder(uint256 nonce) external {
invalidateOrderNonce(msg.sender, nonce, true);
}
function isLimitOrderNonceValid(address taker, uint256 nonce) external view returns (bool) {
uint256 invalidatorSlot = nonce >> 8;
uint256 invalidatorBit = 1 << (nonce & 0xff);
return (limitOrdersNonces[taker][invalidatorSlot] & invalidatorBit) == 0;
}
function invalidateOrderNonce(address taker, uint256 nonce, bool isLimitOrder) private {
require(nonce != 0, "ZERO_NONCE");
uint256 invalidatorSlot = nonce >> 8;
uint256 invalidatorBit = 1 << (nonce & 0xff);
mapping(uint256 => uint256) storage invalidNonces = isLimitOrder ? limitOrdersNonces[taker] : standardNonces[taker];
uint256 invalidator = invalidNonces[invalidatorSlot];
require(invalidator & invalidatorBit != invalidatorBit, "INVALID_NONCE");
invalidNonces[invalidatorSlot] = invalidator | invalidatorBit;
}
function validateIncreasedAmounts(
uint256[] calldata increasedAmounts, uint256[] calldata initialAmounts
) internal returns (uint256[] calldata){
if (increasedAmounts.length == 0) {
return initialAmounts;
}
require(increasedAmounts.length == initialAmounts.length, "INVALID_INCREASED_AMOUNTS_LENGTH");
for (uint256 i; i < increasedAmounts.length; ++i) {
require(increasedAmounts[i] >= initialAmounts[i], "INVALID_INCREASED_AMOUNTS");
}
return increasedAmounts;
}
function validateBatchOrders(
JamOrder.Data[] calldata orders, JamHooks.Def[] calldata hooks, Signature.TypedSignature[] calldata signatures,
Signature.TakerPermitsInfo[] calldata takersPermitsInfo, bool[] calldata takersPermitsUsage, uint16[] calldata curFillPercents
) internal {
bool isMaxFill = curFillPercents.length == 0;
bool noHooks = hooks.length == 0;
bool allTakersWithoutPermits = takersPermitsUsage.length == 0;
require(orders.length == signatures.length, "INVALID_SIGNATURES_LENGTH");
require(orders.length == takersPermitsUsage.length || allTakersWithoutPermits, "INVALID_TAKERS_PERMITS_USAGE_LENGTH");
require(orders.length == hooks.length || noHooks, "INVALID_HOOKS_LENGTH");
require(orders.length == curFillPercents.length || isMaxFill, "INVALID_FILL_PERCENTS_LENGTH");
uint takersWithPermits;
for (uint i; i < orders.length; ++i) {
require(orders[i].receiver != address(this), "INVALID_RECEIVER_FOR_BATCH_SETTLE");
validateOrder(
orders[i], noHooks ? JamHooks.Def(new JamInteraction.Data[](0), new JamInteraction.Data[](0)) : hooks[i],
signatures[i], isMaxFill ? BMath.HUNDRED_PERCENT : curFillPercents[i]
);
if (!allTakersWithoutPermits && takersPermitsUsage[i]){
++takersWithPermits;
}
}
require(takersPermitsInfo.length == takersWithPermits, "INVALID_TAKERS_PERMITS_LENGTH");
}
}
文件 25 的 30:JamTransfer.sol
pragma solidity ^0.8.17;
import "../libraries/JamOrder.sol";
import "../libraries/common/BMath.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC721/IERC721.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC1155/IERC1155.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract JamTransfer {
event NativeTransfer(address indexed receiver, uint256 amount);
using SafeERC20 for IERC20;
function transferTokensFromContract(
address[] calldata tokens,
uint256[] memory amounts,
uint256[] calldata nftIds,
bytes calldata tokenTransferTypes,
address receiver,
uint16 fillPercent,
bool transferExactAmounts
) internal {
uint nftInd;
for (uint i; i < tokens.length; ++i) {
if (tokenTransferTypes[i] == Commands.SIMPLE_TRANSFER) {
uint tokenBalance = IERC20(tokens[i]).balanceOf(address(this));
uint partialFillAmount = BMath.getPercentage(amounts[i], fillPercent);
require(tokenBalance >= partialFillAmount, "INVALID_OUTPUT_TOKEN_BALANCE");
IERC20(tokens[i]).safeTransfer(receiver, transferExactAmounts ? partialFillAmount : tokenBalance);
} else if (tokenTransferTypes[i] == Commands.NATIVE_TRANSFER){
require(tokens[i] == JamOrder.NATIVE_TOKEN, "INVALID_NATIVE_TOKEN");
uint tokenBalance = address(this).balance;
uint partialFillAmount = BMath.getPercentage(amounts[i], fillPercent);
require(tokenBalance >= partialFillAmount, "INVALID_OUTPUT_NATIVE_BALANCE");
(bool sent, ) = payable(receiver).call{value: transferExactAmounts ? partialFillAmount : tokenBalance}("");
require(sent, "FAILED_TO_SEND_ETH");
emit NativeTransfer(receiver, transferExactAmounts ? partialFillAmount : tokenBalance);
} else if (tokenTransferTypes[i] == Commands.NFT_ERC721_TRANSFER) {
uint tokenBalance = IERC721(tokens[i]).balanceOf(address(this));
require(amounts[i] == 1 && tokenBalance >= 1, "INVALID_OUTPUT_ERC721_AMOUNT");
IERC721(tokens[i]).safeTransferFrom(address(this), receiver, nftIds[nftInd++]);
} else if (tokenTransferTypes[i] == Commands.NFT_ERC1155_TRANSFER) {
uint tokenBalance = IERC1155(tokens[i]).balanceOf(address(this), nftIds[nftInd]);
require(tokenBalance >= amounts[i], "INVALID_OUTPUT_ERC1155_BALANCE");
IERC1155(tokens[i]).safeTransferFrom(
address(this), receiver, nftIds[nftInd++], transferExactAmounts ? amounts[i] : tokenBalance, ""
);
} else {
revert("INVALID_TRANSFER_TYPE");
}
}
require(nftInd == nftIds.length, "INVALID_BUY_NFT_IDS_LENGTH");
}
function transferNativeFromContract(address receiver, uint256 amount) public {
(bool sent, ) = payable(receiver).call{value: amount}("");
require(sent, "FAILED_TO_SEND_ETH");
}
function calculateNewAmounts(
uint256 curInd,
JamOrder.Data[] calldata orders,
uint16[] memory fillPercents
) internal returns (uint256[] memory) {
JamOrder.Data calldata curOrder = orders[curInd];
uint256[] memory newAmounts = new uint256[](curOrder.buyTokens.length);
uint16 curFillPercent = fillPercents.length == 0 ? BMath.HUNDRED_PERCENT : fillPercents[curInd];
for (uint i; i < curOrder.buyTokens.length; ++i) {
if (curOrder.buyTokenTransfers[i] == Commands.SIMPLE_TRANSFER || curOrder.buyTokenTransfers[i] == Commands.NATIVE_TRANSFER) {
uint256 fullAmount;
for (uint j = curInd; j < orders.length; ++j) {
for (uint k; k < orders[j].buyTokens.length; ++k) {
if (orders[j].buyTokens[k] == curOrder.buyTokens[i]) {
fullAmount += orders[j].buyAmounts[k];
require(fillPercents.length == 0 || curFillPercent == fillPercents[j], "DIFF_FILL_PERCENT_FOR_SAME_TOKEN");
}
}
}
uint256 tokenBalance = curOrder.buyTokenTransfers[i] == Commands.NATIVE_TRANSFER ?
address(this).balance : IERC20(curOrder.buyTokens[i]).balanceOf(address(this));
newAmounts[i] = BMath.getInvertedPercentage(tokenBalance * curOrder.buyAmounts[i] / fullAmount, curFillPercent);
if (newAmounts[i] < curOrder.buyAmounts[i]) {
newAmounts[i] = curOrder.buyAmounts[i];
}
} else {
newAmounts[i] = curOrder.buyAmounts[i];
}
}
return newAmounts;
}
function hasDuplicate(
address[] calldata tokens, uint256[] calldata nftIds, bytes calldata tokenTransferTypes
) internal pure returns (bool) {
if (tokens.length == 0) {
return false;
}
uint curNftInd;
for (uint i; i < tokens.length - 1; ++i) {
uint tmpNftInd = curNftInd;
for (uint j = i + 1; j < tokens.length; ++j) {
if (tokenTransferTypes[j] == Commands.NFT_ERC721_TRANSFER || tokenTransferTypes[j] == Commands.NFT_ERC1155_TRANSFER){
++tmpNftInd;
}
if (tokens[i] == tokens[j]) {
if (tokenTransferTypes[i] == Commands.NFT_ERC721_TRANSFER ||
tokenTransferTypes[i] == Commands.NFT_ERC1155_TRANSFER){
if (nftIds[curNftInd] == nftIds[tmpNftInd]){
return true;
}
} else {
return true;
}
}
}
if (tokenTransferTypes[i] == Commands.NFT_ERC721_TRANSFER || tokenTransferTypes[i] == Commands.NFT_ERC1155_TRANSFER){
++curNftInd;
}
}
return false;
}
}
文件 26 的 30:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
}
文件 27 的 30:SafeCast160.sol
pragma solidity ^0.8.17;
library SafeCast160 {
error UnsafeCast();
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) revert UnsafeCast();
return uint160(value);
}
}
文件 28 的 30:SafeERC20.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 29 的 30:Signature.sol
pragma solidity ^0.8.17;
library Signature {
enum Type {
NONE,
EIP712,
EIP1271,
ETHSIGN
}
struct TypedSignature {
Type signatureType;
bytes signatureBytes;
}
struct TakerPermitsInfo {
bytes[] permitSignatures;
bytes signatureBytesPermit2;
uint48[] noncesPermit2;
uint48 deadline;
}
function getRsv(bytes memory sig) internal pure returns (bytes32, bytes32, uint8){
require(sig.length == 65, "Invalid signature length");
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(sig, 32))
s := mload(add(sig, 64))
v := and(mload(add(sig, 65)), 255)
}
if (v < 27) v += 27;
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "Invalid sig value S");
require(v == 27 || v == 28, "Invalid sig value V");
return (r, s, v);
}
}
文件 30 的 30:draft-IERC20Permit.sol
pragma solidity ^0.8.0;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
{
"compilationTarget": {
"src/JamSettlement.sol": "JamSettlement"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200000
},
"remappings": [],
"viaIR": true
}
[{"inputs":[{"internalType":"address","name":"_permit2","type":"address"},{"internalType":"address","name":"_daiAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"NativeTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Settlement","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EIP712_DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"JAM_ORDER_TYPE_HASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceManager","outputs":[{"internalType":"contract IJamBalanceManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"cancelLimitOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def","name":"hooks","type":"tuple"}],"name":"hashHooks","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data","name":"order","type":"tuple"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"}],"name":"hashOrder","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"isLimitOrderNonceValid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data","name":"order","type":"tuple"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature","name":"signature","type":"tuple"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"interactions","type":"tuple[]"},{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def","name":"hooks","type":"tuple"},{"components":[{"internalType":"address","name":"balanceRecipient","type":"address"},{"internalType":"uint16","name":"curFillPercent","type":"uint16"}],"internalType":"struct ExecInfo.SolverData","name":"solverData","type":"tuple"}],"name":"settle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data[]","name":"orders","type":"tuple[]"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature[]","name":"signatures","type":"tuple[]"},{"components":[{"internalType":"bytes[]","name":"permitSignatures","type":"bytes[]"},{"internalType":"bytes","name":"signatureBytesPermit2","type":"bytes"},{"internalType":"uint48[]","name":"noncesPermit2","type":"uint48[]"},{"internalType":"uint48","name":"deadline","type":"uint48"}],"internalType":"struct Signature.TakerPermitsInfo[]","name":"takersPermitsInfo","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"interactions","type":"tuple[]"},{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def[]","name":"hooks","type":"tuple[]"},{"components":[{"internalType":"address","name":"balanceRecipient","type":"address"},{"internalType":"uint16[]","name":"curFillPercents","type":"uint16[]"},{"internalType":"bool[]","name":"takersPermitsUsage","type":"bool[]"},{"internalType":"bool","name":"transferExactAmounts","type":"bool"}],"internalType":"struct ExecInfo.BatchSolverData","name":"solverData","type":"tuple"}],"name":"settleBatch","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data","name":"order","type":"tuple"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature","name":"signature","type":"tuple"},{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def","name":"hooks","type":"tuple"},{"components":[{"internalType":"uint256[]","name":"increasedBuyAmounts","type":"uint256[]"},{"internalType":"uint16","name":"curFillPercent","type":"uint16"}],"internalType":"struct ExecInfo.MakerData","name":"makerData","type":"tuple"}],"name":"settleInternal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data","name":"order","type":"tuple"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature","name":"signature","type":"tuple"},{"components":[{"internalType":"bytes[]","name":"permitSignatures","type":"bytes[]"},{"internalType":"bytes","name":"signatureBytesPermit2","type":"bytes"},{"internalType":"uint48[]","name":"noncesPermit2","type":"uint48[]"},{"internalType":"uint48","name":"deadline","type":"uint48"}],"internalType":"struct Signature.TakerPermitsInfo","name":"takerPermitsInfo","type":"tuple"},{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def","name":"hooks","type":"tuple"},{"components":[{"internalType":"uint256[]","name":"increasedBuyAmounts","type":"uint256[]"},{"internalType":"uint16","name":"curFillPercent","type":"uint16"}],"internalType":"struct ExecInfo.MakerData","name":"makerData","type":"tuple"}],"name":"settleInternalWithPermitsSignatures","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"taker","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"address","name":"executor","type":"address"},{"internalType":"uint16","name":"minFillPercent","type":"uint16"},{"internalType":"bytes32","name":"hooksHash","type":"bytes32"},{"internalType":"address[]","name":"sellTokens","type":"address[]"},{"internalType":"address[]","name":"buyTokens","type":"address[]"},{"internalType":"uint256[]","name":"sellAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"buyAmounts","type":"uint256[]"},{"internalType":"uint256[]","name":"sellNFTIds","type":"uint256[]"},{"internalType":"uint256[]","name":"buyNFTIds","type":"uint256[]"},{"internalType":"bytes","name":"sellTokenTransfers","type":"bytes"},{"internalType":"bytes","name":"buyTokenTransfers","type":"bytes"}],"internalType":"struct JamOrder.Data","name":"order","type":"tuple"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature","name":"signature","type":"tuple"},{"components":[{"internalType":"bytes[]","name":"permitSignatures","type":"bytes[]"},{"internalType":"bytes","name":"signatureBytesPermit2","type":"bytes"},{"internalType":"uint48[]","name":"noncesPermit2","type":"uint48[]"},{"internalType":"uint48","name":"deadline","type":"uint48"}],"internalType":"struct Signature.TakerPermitsInfo","name":"takerPermitsInfo","type":"tuple"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"interactions","type":"tuple[]"},{"components":[{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"beforeSettle","type":"tuple[]"},{"components":[{"internalType":"bool","name":"result","type":"bool"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct JamInteraction.Data[]","name":"afterSettle","type":"tuple[]"}],"internalType":"struct JamHooks.Def","name":"hooks","type":"tuple"},{"components":[{"internalType":"address","name":"balanceRecipient","type":"address"},{"internalType":"uint16","name":"curFillPercent","type":"uint16"}],"internalType":"struct ExecInfo.SolverData","name":"solverData","type":"tuple"}],"name":"settleWithPermitsSignatures","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferNativeFromContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"validationAddress","type":"address"},{"internalType":"bytes32","name":"hash","type":"bytes32"},{"components":[{"internalType":"enum Signature.Type","name":"signatureType","type":"uint8"},{"internalType":"bytes","name":"signatureBytes","type":"bytes"}],"internalType":"struct Signature.TypedSignature","name":"signature","type":"tuple"}],"name":"validateSignature","outputs":[],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]