文件 1 的 4:IERC20.sol
pragma solidity ^0.7.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, 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 sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 2 的 4:Oven.sol
pragma solidity ^0.7.1;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/PieRecipe.sol";
contract Oven {
using SafeMath for uint256;
event Deposit(address user, uint256 amount);
event WithdrawETH(address user, uint256 amount, address receiver);
event WithdrawOuput(address user, uint256 amount, address receiver);
event Bake(address user, uint256 amount, uint256 price);
mapping(address => uint256) public ethBalanceOf;
mapping(address => uint256) public outputBalanceOf;
address public controller;
IERC20 public pie;
PieRecipe public recipe;
uint256 public cap;
constructor(
address _controller,
address _pie,
address _recipe
) public {
controller = _controller;
pie = IERC20(_pie);
recipe = PieRecipe(_recipe);
}
modifier ovenIsReady {
require(address(pie) != address(0), "PIE_NOT_SET");
require(address(recipe) != address(0), "RECIPE_NOT_SET");
_;
}
modifier controllerOnly {
require(msg.sender == controller, "NOT_CONTROLLER");
_;
}
function bake(
address[] calldata _receivers,
uint256 _outputAmount,
uint256 _maxPrice
) public ovenIsReady controllerOnly {
uint256 realPrice = recipe.calcToPie(address(pie), _outputAmount);
require(realPrice <= _maxPrice, "PRICE_ERROR");
uint256 totalInputAmount = 0;
for (uint256 i = 0; i < _receivers.length; i++) {
uint256 userAmount = ethBalanceOf[_receivers[i]];
if (totalInputAmount == realPrice) {
break;
} else if (totalInputAmount.add(userAmount) <= realPrice) {
totalInputAmount = totalInputAmount.add(userAmount);
} else {
userAmount = realPrice.sub(totalInputAmount);
totalInputAmount = totalInputAmount.add(userAmount);
}
ethBalanceOf[_receivers[i]] = ethBalanceOf[_receivers[i]].sub(
userAmount
);
uint256 userBakeAmount = _outputAmount.mul(userAmount).div(
realPrice
);
outputBalanceOf[_receivers[i]] = outputBalanceOf[_receivers[i]].add(
userBakeAmount
);
emit Bake(_receivers[i], userBakeAmount, userAmount);
}
require(totalInputAmount == realPrice, "INSUFFICIENT_FUNDS");
recipe.toPie{value: realPrice}(address(pie), _outputAmount);
}
function deposit() public payable ovenIsReady {
ethBalanceOf[msg.sender] = ethBalanceOf[msg.sender].add(msg.value);
require(address(this).balance <= cap, "MAX_CAP");
emit Deposit(msg.sender, msg.value);
}
receive() external payable {
deposit();
}
function withdrawAll(address payable _receiver) external ovenIsReady {
withdrawAllETH(_receiver);
withdrawOutput(_receiver);
}
function withdrawAllETH(address payable _receiver) public ovenIsReady {
withdrawETH(ethBalanceOf[msg.sender], _receiver);
}
function withdrawETH(uint256 _amount, address payable _receiver)
public
ovenIsReady
{
ethBalanceOf[msg.sender] = ethBalanceOf[msg.sender].sub(_amount);
_receiver.transfer(_amount);
emit WithdrawETH(msg.sender, _amount, _receiver);
}
function withdrawOutput(address _receiver) public ovenIsReady {
uint256 _amount = outputBalanceOf[msg.sender];
outputBalanceOf[msg.sender] = 0;
pie.transfer(_receiver, _amount);
emit WithdrawOuput(msg.sender, _amount, _receiver);
}
function setCap(uint256 _cap) external controllerOnly {
cap = _cap;
}
function setController(address _controller) external controllerOnly {
controller = _controller;
}
function setPie(address _pie) public controllerOnly {
require(address(pie) == address(0), "PIE_ALREADY_SET");
pie = IERC20(_pie);
}
function setRecipe(address _recipe) public controllerOnly {
require(address(recipe) == address(0), "RECIPE_ALREADY_SET");
recipe = PieRecipe(_recipe);
}
function setPieAndRecipe(address _pie, address _recipe) external {
setPie(_pie);
setRecipe(_recipe);
}
function getCap() external view returns (uint256) {
return cap;
}
function saveToken(address _token) external {
require(_token != address(pie), "INVALID_TOKEN");
IERC20 token = IERC20(_token);
token.transfer(
address(0x4efD8CEad66bb0fA64C8d53eBE65f31663199C6d),
token.balanceOf(address(this))
);
}
}
文件 3 的 4:PieRecipe.sol
pragma solidity ^0.7.1;
interface PieRecipe {
function toPie(address _pie, uint256 _poolAmount) external payable;
function calcToPie(address _pie, uint256 _poolAmount)
external
view
returns (uint256);
}
文件 4 的 4:SafeMath.sol
pragma solidity ^0.7.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
{
"compilationTarget": {
"contracts/oven/Oven.sol": "Oven"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_controller","type":"address"},{"internalType":"address","name":"_pie","type":"address"},{"internalType":"address","name":"_recipe","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"}],"name":"Bake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"WithdrawETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"WithdrawOuput","type":"event"},{"inputs":[{"internalType":"address[]","name":"_receivers","type":"address[]"},{"internalType":"uint256","name":"_outputAmount","type":"uint256"},{"internalType":"uint256","name":"_maxPrice","type":"uint256"}],"name":"bake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"ethBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"outputBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pie","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recipe","outputs":[{"internalType":"contract PieRecipe","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"saveToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cap","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_controller","type":"address"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pie","type":"address"}],"name":"setPie","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pie","type":"address"},{"internalType":"address","name":"_recipe","type":"address"}],"name":"setPieAndRecipe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipe","type":"address"}],"name":"setRecipe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_receiver","type":"address"}],"name":"withdrawAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_receiver","type":"address"}],"name":"withdrawAllETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address payable","name":"_receiver","type":"address"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"withdrawOutput","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]