编译器
0.8.11+commit.d7f03943
文件 1 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 10:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount)
public
virtual
returns (bool)
{
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount)
public
virtual
returns (bool)
{
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max)
allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(
recoveredAddress != address(0) && recoveredAddress == owner,
"INVALID_SIGNER"
);
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return
block.chainid == INITIAL_CHAIN_ID
? INITIAL_DOMAIN_SEPARATOR
: computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 3 的 10:IPortalFactory.sol
pragma solidity 0.8.11;
import "./IPortalRegistry.sol";
interface IPortalFactory {
function fee() external view returns (uint256 fee);
function registry() external view returns (IPortalRegistry registry);
}
文件 4 的 10:IPortalRegistry.sol
pragma solidity 0.8.11;
enum PortalType {
IN,
OUT
}
interface IPortalRegistry {
function addPortal(
address portal,
PortalType portalType,
bytes32 protocolId
) external;
function addPortalFactory(
address portalFactory,
PortalType portalType,
bytes32 protocolId
) external;
function removePortal(bytes32 protocolId, PortalType portalType) external;
function owner() external view returns (address owner);
function registrars(address origin) external view returns (bool isDeployer);
function collector() external view returns (address collector);
function isPortal(address portal) external view returns (bool isPortal);
}
文件 5 的 10:IVault.sol
pragma solidity 0.8.11;
interface IVault {
function deposit(uint256 _amount) external payable;
function withdraw(uint256 _amount) external;
function permit(
address owner,
address spender,
uint256 amount,
uint256 expiry,
bytes calldata signature
) external returns (bool);
}
文件 6 的 10:IWETH.sol
pragma solidity 0.8.11;
interface IWETH {
function deposit() external payable;
function withdraw(uint256 wad) external;
}
文件 7 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 8 的 10:PortalBaseV1_1.sol
pragma solidity 0.8.11;
import "@openzeppelin/contracts/access/Ownable.sol";
import "../libraries/solmate/utils/SafeTransferLib.sol";
import "../interface/IWETH.sol";
import "../interface/IPortalFactory.sol";
import "../interface/IPortalRegistry.sol";
abstract contract PortalBaseV1_1 is Ownable {
using SafeTransferLib for address;
using SafeTransferLib for ERC20;
bool public paused;
uint256 public fee;
IPortalRegistry public registry;
address public immutable exchange;
address public immutable wrappedNetworkToken;
modifier pausable() {
require(!paused, "Paused");
_;
}
constructor(
bytes32 protocolId,
PortalType portalType,
IPortalRegistry _registry,
address _exchange,
address _wrappedNetworkToken,
uint256 _fee
) {
wrappedNetworkToken = _wrappedNetworkToken;
setFee(_fee);
exchange = _exchange;
registry = _registry;
registry.addPortal(address(this), portalType, protocolId);
transferOwnership(registry.owner());
}
function _transferFromCaller(address token, uint256 quantity)
internal
virtual
returns (uint256)
{
if (token == address(0)) {
require(
msg.value > 0 && msg.value == quantity,
"Invalid quantity or msg.value"
);
return msg.value;
}
require(
quantity > 0 && msg.value == 0,
"Invalid quantity or msg.value"
);
ERC20(token).safeTransferFrom(msg.sender, address(this), quantity);
return quantity;
}
function _getFeeAmount(uint256 quantity, uint256 feeBps)
internal
view
returns (uint256)
{
return
registry.isPortal(msg.sender)
? quantity
: quantity - (quantity * feeBps) / 10000;
}
function _execute(
address sellToken,
uint256 sellAmount,
address buyToken,
address target,
bytes memory data
) internal virtual returns (uint256 amountBought) {
if (sellToken == buyToken) {
return sellAmount;
}
if (sellToken == address(0) && buyToken == wrappedNetworkToken) {
IWETH(wrappedNetworkToken).deposit{ value: sellAmount }();
return sellAmount;
}
if (sellToken == wrappedNetworkToken && buyToken == address(0)) {
IWETH(wrappedNetworkToken).withdraw(sellAmount);
return sellAmount;
}
uint256 valueToSend;
if (sellToken == address(0)) {
valueToSend = sellAmount;
} else {
_approve(sellToken, target, sellAmount);
}
uint256 initialBalance = _getBalance(address(this), buyToken);
require(
target == exchange || registry.isPortal(target),
"Unauthorized target"
);
(bool success, bytes memory returnData) = target.call{
value: valueToSend
}(data);
require(success, string(returnData));
amountBought = _getBalance(address(this), buyToken) - initialBalance;
require(amountBought > 0, "Invalid execution");
}
function _getBalance(address account, address token)
internal
view
returns (uint256)
{
if (token == address(0)) {
return account.balance;
} else {
return ERC20(token).balanceOf(account);
}
}
function _approve(
address token,
address spender,
uint256 amount
) internal {
ERC20 _token = ERC20(token);
_token.safeApprove(spender, 0);
_token.safeApprove(spender, amount);
}
function collect(address[] calldata tokens) external {
address collector = registry.collector();
for (uint256 i = 0; i < tokens.length; i++) {
uint256 qty;
if (tokens[i] == address(0)) {
qty = address(this).balance;
collector.safeTransferETH(qty);
} else {
qty = ERC20(tokens[i]).balanceOf(address(this));
ERC20(tokens[i]).safeTransfer(collector, qty);
}
}
}
function pause() external onlyOwner {
paused = !paused;
}
function setFee(uint256 _fee) public onlyOwner {
require(_fee >= 6 && _fee <= 100, "Invalid Fee");
fee = _fee;
}
function updateRegistry(IPortalRegistry _registry) external onlyOwner {
registry = _registry;
}
receive() external payable {
require(msg.sender != tx.origin);
}
}
文件 9 的 10:SafeTransferLib.sol
pragma solidity >=0.8.0;
import { ERC20 } from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0x23b872dd00000000000000000000000000000000000000000000000000000000
)
mstore(add(freeMemoryPointer, 4), from)
mstore(add(freeMemoryPointer, 36), to)
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(
and(eq(mload(0), 1), gt(returndatasize(), 31)),
iszero(returndatasize())
),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0xa9059cbb00000000000000000000000000000000000000000000000000000000
)
mstore(add(freeMemoryPointer, 4), to)
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(
and(eq(mload(0), 1), gt(returndatasize(), 31)),
iszero(returndatasize())
),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(
freeMemoryPointer,
0x095ea7b300000000000000000000000000000000000000000000000000000000
)
mstore(add(freeMemoryPointer, 4), to)
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(
and(eq(mload(0), 1), gt(returndatasize(), 31)),
iszero(returndatasize())
),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 10 的 10:YearnPortalOut.sol
pragma solidity 0.8.11;
import "../base/PortalBaseV1_1.sol";
import "./interface/IVault.sol";
error InsufficientBuy(uint256 buyAmount, uint256 minBuyAmount);
contract YearnPortalOut is PortalBaseV1_1 {
using SafeTransferLib for address;
using SafeTransferLib for ERC20;
event PortalOut(
address sellToken,
uint256 sellAmount,
address buyToken,
uint256 buyAmount,
uint256 fee,
address indexed sender,
address indexed partner
);
constructor(
bytes32 protocolId,
PortalType portalType,
IPortalRegistry registry,
address exchange,
address wrappedNetworkToken,
uint256 fee
)
PortalBaseV1_1(
protocolId,
portalType,
registry,
exchange,
wrappedNetworkToken,
fee
)
{}
function portalOut(
address sellToken,
uint256 sellAmount,
address intermediateToken,
address buyToken,
uint256 minBuyAmount,
address target,
bytes calldata data,
address partner
) public payable pausable returns (uint256 buyAmount) {
uint256 amount = _transferFromCaller(sellToken, sellAmount);
uint256 balance = _getBalance(address(this), intermediateToken);
IVault(sellToken).withdraw(amount);
amount = _getBalance(address(this), intermediateToken) - balance;
buyAmount = _execute(intermediateToken, amount, buyToken, target, data);
buyAmount = _getFeeAmount(buyAmount, fee);
if (buyAmount < minBuyAmount)
revert InsufficientBuy(buyAmount, minBuyAmount);
buyToken == address(0)
? msg.sender.safeTransferETH(buyAmount)
: ERC20(buyToken).safeTransfer(msg.sender, buyAmount);
emit PortalOut(
sellToken,
sellAmount,
buyToken,
buyAmount,
fee,
msg.sender,
partner
);
}
function portalOutWithPermit(
address sellToken,
uint256 sellAmount,
address intermediateToken,
address buyToken,
uint256 minBuyAmount,
address target,
bytes calldata data,
address partner,
bytes calldata signature
) external payable pausable returns (uint256 buyAmount) {
_permit(sellToken, sellAmount, signature);
return
portalOut(
sellToken,
sellAmount,
intermediateToken,
buyToken,
minBuyAmount,
target,
data,
partner
);
}
function _permit(
address sellToken,
uint256 sellAmount,
bytes calldata signature
) internal {
bool success = IVault(sellToken).permit(
msg.sender,
address(this),
sellAmount,
0,
signature
);
require(success, "Could Not Permit");
}
}
{
"compilationTarget": {
"contracts/yearn/YearnPortalOut.sol": "YearnPortalOut"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"bytes32","name":"protocolId","type":"bytes32"},{"internalType":"enum PortalType","name":"portalType","type":"uint8"},{"internalType":"contract IPortalRegistry","name":"registry","type":"address"},{"internalType":"address","name":"exchange","type":"address"},{"internalType":"address","name":"wrappedNetworkToken","type":"address"},{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"buyAmount","type":"uint256"},{"internalType":"uint256","name":"minBuyAmount","type":"uint256"}],"name":"InsufficientBuy","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sellToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"sellAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"buyToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"buyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"partner","type":"address"}],"name":"PortalOut","type":"event"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"collect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchange","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"address","name":"intermediateToken","type":"address"},{"internalType":"address","name":"buyToken","type":"address"},{"internalType":"uint256","name":"minBuyAmount","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"partner","type":"address"}],"name":"portalOut","outputs":[{"internalType":"uint256","name":"buyAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"sellToken","type":"address"},{"internalType":"uint256","name":"sellAmount","type":"uint256"},{"internalType":"address","name":"intermediateToken","type":"address"},{"internalType":"address","name":"buyToken","type":"address"},{"internalType":"uint256","name":"minBuyAmount","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"address","name":"partner","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"portalOutWithPermit","outputs":[{"internalType":"uint256","name":"buyAmount","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract IPortalRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPortalRegistry","name":"_registry","type":"address"}],"name":"updateRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"wrappedNetworkToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]