编译器
0.8.23+commit.f704f362
文件 1 的 18: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 的 18:Dice.sol
pragma solidity ^0.8.13;
import "./UniformRandomNumber.sol";
library Dice {
function rollDiceSet(uint8 resultSetSize, uint upperLimit, uint seed) internal pure returns (uint) {
uint sum;
for (uint i = 0;i < resultSetSize;i++) {
sum += UniformRandomNumber.uniform(
uint(keccak256(abi.encode(seed, i))),
upperLimit
) + 1;
}
return sum;
}
}
文件 3 的 18: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);
}
}
文件 4 的 18:IUniswapV3Pool.sol
pragma solidity >=0.5.0;
import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}
文件 5 的 18:IUniswapV3PoolActions.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolActions {
function initialize(uint160 sqrtPriceX96) external;
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}
文件 6 的 18:IUniswapV3PoolDerivedState.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolDerivedState {
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
);
}
文件 7 的 18:IUniswapV3PoolEvents.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolEvents {
event Initialize(uint160 sqrtPriceX96, int24 tick);
event Mint(
address sender,
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
event Collect(
address indexed owner,
address recipient,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount0,
uint128 amount1
);
event Burn(
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
event Flash(
address indexed sender,
address indexed recipient,
uint256 amount0,
uint256 amount1,
uint256 paid0,
uint256 paid1
);
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}
文件 8 的 18:IUniswapV3PoolImmutables.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolImmutables {
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function fee() external view returns (uint24);
function tickSpacing() external view returns (int24);
function maxLiquidityPerTick() external view returns (uint128);
}
文件 9 的 18:IUniswapV3PoolOwnerActions.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolOwnerActions {
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
function collectProtocol(
address recipient,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
}
文件 10 的 18:IUniswapV3PoolState.sol
pragma solidity >=0.5.0;
interface IUniswapV3PoolState {
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
function feeGrowthGlobal0X128() external view returns (uint256);
function feeGrowthGlobal1X128() external view returns (uint256);
function protocolFees() external view returns (uint128 token0, uint128 token1);
function liquidity() external view returns (uint128);
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
function tickBitmap(int16 wordPosition) external view returns (uint256);
function positions(bytes32 key)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
function observations(uint256 index)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
}
文件 11 的 18:IUniswapV3SwapCallback.sol
pragma solidity >=0.5.0;
interface IUniswapV3SwapCallback {
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}
文件 12 的 18:IV3SwapRouter.sol
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
interface IV3SwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 amountIn;
uint256 amountOutMinimum;
}
function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 amountOut;
uint256 amountInMaximum;
}
function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn);
}
文件 13 的 18:KingOfTheDegens.sol
pragma solidity ^0.8.13;
import {Owned} from "solmate/auth/Owned.sol";
import {Pausable} from "openzeppelin/security/Pausable.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {Dice} from "./lib/Dice.sol";
import {Trustus} from "trustus/Trustus.sol";
import 'swap-router-contracts/interfaces/IV3SwapRouter.sol';
import 'v3-core/contracts/interfaces/IUniswapV3Pool.sol';
contract KingOfTheDegens is Owned, Pausable, Trustus {
uint256 public immutable gameDurationBlocks;
uint256 public immutable minPlayAmount;
uint256 public immutable protocolFee;
uint256 public stormFrequencyBlocks;
uint256 public immutable redeemAfterGameEndedBlocks;
uint256 public immutable totalPointsPerBlock = 1e18;
ERC20 public immutable degenToken = ERC20(0x4ed4E862860beD51a9570b96d89aF5E1B0Efefed);
address public immutable WETH = 0x4200000000000000000000000000000000000006;
IV3SwapRouter swapRouter02 = IV3SwapRouter(0x2626664c2603336E57B271c5C0b26F421741e481);
IUniswapV3Pool degenPool = IUniswapV3Pool(0xc9034c3E7F58003E6ae0C8438e7c8f4598d5ACAA);
bytes32 public immutable TRUSTUS_STORM = 0xeb8042f25b217795f608170833efd195ff101fb452e6483bf545403bf6d8f49b;
mapping(CourtRole => uint256) public courtBps;
mapping(address => uint256) public stormBlock;
mapping(address => CourtRole) public courtRoles;
mapping(address => uint256) public pointsBalance;
mapping(address => uint256) public roleStartBlock;
uint256 public storms;
uint256 public gameStartBlock;
uint256 public protocolFeeBalance;
uint256 public gameAssets;
address[1] public king;
address[2] public lords;
address[3] public knights;
address[4] public townsfolk;
enum CourtRole {
None,
King,
Lord,
Knight,
Townsfolk
}
uint8[3] public roleRanges;
event StormTheCastle(
address indexed accountAddress,
uint8 indexed courtRole,
address indexed outAddress,
uint256 fid
);
event Redeemed(address indexed accountAddress, uint256 indexed amountRedeemed, uint256 indexed pointsRedeemed);
error BadZeroAddress();
error GameNotActive(uint256 gameStartBlock, uint256 gameEndBlock, uint256 currentBlock);
error GameIsActive();
error RedeemStillActive(uint256 redeemEndedBlock);
error RedeemEnded();
error InsufficientFunds(uint256 valueSent);
error TooFrequentStorms(uint256 nextBlockAllowed, uint256 currentBlockNumber);
error AlreadyCourtMember(address accountAddress, CourtRole courtRole);
error BadCourtRole(CourtRole courtRole);
error InsufficientBalance();
error CourtRoleMismatch(address accountAddress, CourtRole courtRole, CourtRole expectedCourtRole);
error BadCourtRolePercentages(uint8 percentageTotal);
error ArrayLengthMismatch(uint256 length1, uint256 length2);
constructor(
uint256 _gameDurationBlocks,
uint256 _minPlayAmount,
uint256 _protocolFee,
uint256 _stormFrequencyBlocks,
uint256 _redeemAfterGameEndedBlocks,
uint256[4] memory _courtBps,
uint8[4] memory _courtRolePercentages
) Owned(msg.sender) {
gameDurationBlocks = _gameDurationBlocks;
minPlayAmount = _minPlayAmount;
protocolFee = _protocolFee;
stormFrequencyBlocks = _stormFrequencyBlocks;
redeemAfterGameEndedBlocks = _redeemAfterGameEndedBlocks;
_setCourtBps(_courtBps);
_setRoleRanges(_courtRolePercentages);
}
function initGameState(
uint256 _storms,
address[] memory _accountAddresses,
uint256[] memory _points,
uint256[] memory _stormBlocks
) public onlyOwner {
if (isGameActive()) revert GameIsActive();
if (_accountAddresses.length != _points.length) revert ArrayLengthMismatch(_accountAddresses.length, _points.length);
if (_stormBlocks.length != _points.length) revert ArrayLengthMismatch(_stormBlocks.length, _points.length);
storms = _storms;
for (uint256 i = 0;i < _accountAddresses.length;i++) {
pointsBalance[_accountAddresses[i]] = _points[i];
stormBlock[_accountAddresses[i]] = _stormBlocks[i];
}
}
function startGame(
address[1] calldata _king,
address[2] calldata _lords,
address[3] calldata _knights,
address[4] calldata _townsfolk,
uint256 _startBlock
) public onlyOwner {
confirmTheStorm(_king[0], CourtRole.King);
confirmTheStorm(_lords[0], CourtRole.Lord);
confirmTheStorm(_lords[1], CourtRole.Lord);
confirmTheStorm(_knights[0], CourtRole.Knight);
confirmTheStorm(_knights[1], CourtRole.Knight);
confirmTheStorm(_knights[2], CourtRole.Knight);
confirmTheStorm(_townsfolk[0], CourtRole.Townsfolk);
confirmTheStorm(_townsfolk[1], CourtRole.Townsfolk);
confirmTheStorm(_townsfolk[2], CourtRole.Townsfolk);
confirmTheStorm(_townsfolk[3], CourtRole.Townsfolk);
gameStartBlock = _startBlock > 0 ? _startBlock : block.number;
}
function collectProtocolFees() public onlyOwner {
if (protocolFeeBalance == 0) revert InsufficientBalance();
if (!isGameEnded()) revert GameIsActive();
SafeTransferLib.safeTransferETH(msg.sender, protocolFeeBalance);
protocolFeeBalance = 0;
}
function protocolRedeem() public onlyOwner {
uint256 redeemEndedBlock = gameEndBlock() + redeemAfterGameEndedBlocks;
if (block.number < redeemEndedBlock) revert RedeemStillActive(redeemEndedBlock);
SafeTransferLib.safeTransfer(degenToken, msg.sender, degenToken.balanceOf(address(this)));
}
function setIsTrusted(address trustedAddress, bool isTrusted) public onlyOwner {
_setIsTrusted(trustedAddress, isTrusted);
}
function togglePause() public onlyOwner {
if (paused()) {
_unpause();
} else {
_pause();
}
}
function setStormFrequency(uint256 blocks) public onlyOwner {
stormFrequencyBlocks = blocks;
}
function setCourtRolePercentages(uint8[4] memory _courtRolePercentages) public onlyOwner {
_setRoleRanges(_courtRolePercentages);
}
function setCourtBps(uint256[4] memory _courtBps) public onlyOwner {
_setCourtBps(_courtBps);
}
function depositDegenToGameAssets(uint256 degenAmountWei) public {
gameAssets += degenAmountWei;
SafeTransferLib.safeTransferFrom(degenToken, msg.sender, address(this), degenAmountWei);
}
function stormTheCastle(TrustusPacket calldata packet) public payable verifyPacket(TRUSTUS_STORM, packet) whenNotPaused() {
if (msg.sender == address(0)) revert BadZeroAddress();
if (!isGameActive()) revert GameNotActive(gameStartBlock, gameEndBlock(), block.number);
if (msg.value < minPlayAmount) revert InsufficientFunds(msg.value);
if (stormBlock[msg.sender] + stormFrequencyBlocks >= block.number) revert TooFrequentStorms(
stormBlock[msg.sender] + stormFrequencyBlocks,
block.number
);
if (courtRoles[msg.sender] != CourtRole.None) revert AlreadyCourtMember(
msg.sender,
courtRoles[msg.sender]
);
uint256 randomSeed;
uint256 fid;
(randomSeed, fid) = abi.decode(packet.payload, (uint256,uint256));
uint256 degenAmount = convertEthToDegen(msg.value - protocolFee);
CourtRole courtRole = determineCourtRole(msg.sender, randomSeed);
address outAddress = confirmTheStorm(msg.sender, courtRole);
protocolFeeBalance += protocolFee;
gameAssets += degenAmount;
storms++;
stormBlock[msg.sender] = block.number;
emit StormTheCastle(msg.sender, uint8(courtRole), outAddress, fid);
}
function isGameStarted() public view returns (bool) {
return gameStartBlock <= block.number;
}
function isGameEnded() public view returns (bool) {
return gameEndBlock() <= block.number;
}
function isGameActive() public view returns (bool) {
return isGameStarted() && !isGameEnded();
}
function gameEndBlock() public view returns (uint256) {
return gameStartBlock + gameDurationBlocks;
}
function gameLastBlock() public view returns (uint256) {
return gameEndBlock() - 1;
}
function totalAssets() public view returns (uint256) {
return gameAssets;
}
function redeem() public whenNotPaused {
if (isGameActive()) revert GameIsActive();
if (degenToken.balanceOf(address(this)) == 0) revert RedeemEnded();
if (courtRoles[msg.sender] != CourtRole.None) {
stopPointsFlow(msg.sender, courtRoles[msg.sender]);
}
if (pointsBalance[msg.sender] == 0) revert InsufficientBalance();
uint256 degenToSend = convertPoints(pointsBalance[msg.sender]);
SafeTransferLib.safeTransfer(degenToken, msg.sender, degenToSend);
emit Redeemed(msg.sender, degenToSend, pointsBalance[msg.sender]);
pointsBalance[msg.sender] = 0;
}
function totalPoints() public view returns (uint256) {
return gameDurationBlocks * totalPointsPerBlock;
}
function getPointsPerBlock(CourtRole courtRole) public view returns (uint256) {
uint256 bps = courtBps[courtRole];
return (totalPointsPerBlock * bps / 10_000);
}
function determineCourtRole(address accountAddress, uint256 _randomSeed) public view returns (CourtRole) {
uint256 random = Dice.rollDiceSet(
1,
100,
uint256(keccak256(abi.encodePacked(accountAddress, _randomSeed)))
);
if (random >= 1 && random <= roleRanges[0]) {
return CourtRole.King;
} else if (random > roleRanges[0] && random <= roleRanges[1]) {
return CourtRole.Lord;
} else if (random > roleRanges[1] && random <= roleRanges[2]) {
return CourtRole.Knight;
} else {
return CourtRole.Townsfolk;
}
}
function confirmTheStorm(address accountAddress, CourtRole courtRole) private returns(address) {
address outAddress;
if (courtRole == CourtRole.King) {
outAddress = king[0];
switchFlows(king[0], accountAddress, CourtRole.King);
king[0] = accountAddress;
} else if (courtRole == CourtRole.Lord) {
outAddress = lords[0];
switchFlows(lords[0], accountAddress,CourtRole.Lord);
lords[0] = lords[1];
lords[1] = accountAddress;
} else if (courtRole == CourtRole.Knight) {
outAddress = knights[0];
switchFlows(knights[0], accountAddress, CourtRole.Knight);
knights[0] = knights[1];
knights[1] = knights[2];
knights[2] = accountAddress;
} else {
outAddress = townsfolk[0];
switchFlows(townsfolk[0], accountAddress, CourtRole.Townsfolk);
townsfolk[0] = townsfolk[1];
townsfolk[1] = townsfolk[2];
townsfolk[2] = townsfolk[3];
townsfolk[3] = accountAddress;
}
return outAddress;
}
function switchFlows(address oldAddress, address newAddress, CourtRole courtRole) private {
if (oldAddress != address(0)) {
stopPointsFlow(oldAddress, courtRole);
}
startPointsFlow(newAddress, courtRole);
}
function convertEthToDegen(uint256 convertAmountWei) private returns (uint256) {
(uint160 sqrtPriceX96,,,,,,) = degenPool.slot0();
uint256 ethToDegenSpotPrice = getPriceDegenToEth(sqrtPriceX96);
uint256 ethToDegenAmountOut = ethToDegenSpotPrice - (ethToDegenSpotPrice * 100 / 10_000);
uint256 amountOutMinimum = ethToDegenAmountOut * convertAmountWei;
IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter
.ExactInputSingleParams({
tokenIn: WETH,
tokenOut: address(degenToken),
fee: 3000,
recipient: address(this),
amountIn: convertAmountWei,
amountOutMinimum: amountOutMinimum,
sqrtPriceLimitX96: 0
});
return swapRouter02.exactInputSingle{value: convertAmountWei}(params);
}
function getPriceDegenToEth(uint256 sqrtPriceX96) private pure returns (uint256 price) {
uint256 sqrtPriceAdjusted = sqrtPriceX96 / (2 ** 48);
return (sqrtPriceAdjusted * sqrtPriceAdjusted) / (2 ** 96);
}
function stopPointsFlow(address accountAddress, CourtRole courtRole) private {
if (courtRoles[accountAddress] != courtRole) revert CourtRoleMismatch(
accountAddress,
courtRole,
courtRoles[accountAddress]
);
pointsBalance[accountAddress] += calculatePointsEarned(accountAddress, block.number, courtRole);
courtRoles[accountAddress] = CourtRole.None;
roleStartBlock[accountAddress] = 0;
}
function startPointsFlow(address accountAddress, CourtRole courtRole) private {
courtRoles[accountAddress] = courtRole;
roleStartBlock[accountAddress] = block.number;
}
function calculatePointsEarned(
address accountAddress,
uint256 endBlockNumber,
CourtRole courtRole
) public view returns (uint256) {
endBlockNumber = endBlockNumber > gameEndBlock() ? gameEndBlock() : endBlockNumber;
if (roleStartBlock[accountAddress] == 0 || endBlockNumber <= roleStartBlock[accountAddress]) return 0;
return (endBlockNumber - roleStartBlock[accountAddress]) * getPointsPerBlock(courtRole);
}
function convertPoints(uint256 points) public view returns (uint256) {
assert(points <= type(uint256).max / 1e18);
uint256 pointsAdjusted = points * 1e18;
uint256 percentage = pointsAdjusted / totalPoints();
return (totalAssets() * percentage) / 1e18;
}
function getCourtMemberPoints() public view returns (uint256[10] memory) {
address[10] memory courtAddresses = getCourtAddresses();
uint256[10] memory points;
for (uint256 i = 0;i < courtAddresses.length;i++) {
points[i] = pointsBalance[courtAddresses[i]] + calculatePointsEarned(
courtAddresses[i],
block.number,
getCourtRoleFromAddressesIndex(i)
);
}
return points;
}
function getCourtAddresses() public view returns (address[10] memory) {
return [
king[0],
lords[0],
lords[1],
knights[0],
knights[1],
knights[2],
townsfolk[0],
townsfolk[1],
townsfolk[2],
townsfolk[3]
];
}
function getCourtRoleFromAddressesIndex(uint256 index) public pure returns (CourtRole) {
if (index == 0) {
return CourtRole.King;
} else if (index < 3) {
return CourtRole.Lord;
} else if (index < 6) {
return CourtRole.Knight;
} else {
return CourtRole.Townsfolk;
}
}
function _setRoleRanges(uint8[4] memory _percentages) private {
uint8 total = _percentages[0] + _percentages[1] + _percentages[2] + _percentages[3];
if (total != 100) revert BadCourtRolePercentages(total);
roleRanges[0] = _percentages[0];
roleRanges[1] = roleRanges[0] + _percentages[1];
roleRanges[2] = roleRanges[1] + _percentages[2];
}
function _setCourtBps(uint256[4] memory _courtBps) private {
for (uint256 i = 0;i < 4;i++) {
courtBps[CourtRole(i + 1)] = _courtBps[i];
}
}
receive() external payable {}
}
文件 14 的 18:Owned.sol
pragma solidity >=0.8.0;
abstract contract Owned {
event OwnershipTransferred(address indexed user, address indexed newOwner);
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
文件 15 的 18:Pausable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor() {
_paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
return _paused;
}
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 16 的 18: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), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
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), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
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), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
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");
}
}
文件 17 的 18:Trustus.sol
pragma solidity ^0.8.4;
abstract contract Trustus {
struct TrustusPacket {
uint8 v;
bytes32 r;
bytes32 s;
bytes32 request;
uint256 deadline;
bytes payload;
}
error Trustus__InvalidPacket();
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => bool) internal isTrusted;
modifier verifyPacket(bytes32 request, TrustusPacket calldata packet) {
if (!_verifyPacket(request, packet)) revert Trustus__InvalidPacket();
_;
}
constructor() {
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
function _verifyPacket(bytes32 request, TrustusPacket calldata packet)
internal
virtual
returns (bool success)
{
if (block.timestamp > packet.deadline) return false;
if (request != packet.request) return false;
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"VerifyPacket(bytes32 request,uint256 deadline,bytes payload)"
),
packet.request,
packet.deadline,
keccak256(packet.payload)
)
)
)
),
packet.v,
packet.r,
packet.s
);
return (recoveredAddress != address(0)) && isTrusted[recoveredAddress];
}
function _setIsTrusted(address signer, bool isTrusted_) internal virtual {
isTrusted[signer] = isTrusted_;
}
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("Trustus"),
keccak256("1"),
block.chainid,
address(this)
)
);
}
}
文件 18 的 18:UniformRandomNumber.sol
pragma solidity ^0.8.13;
library UniformRandomNumber {
function uniform(uint256 _entropy, uint256 _upperBound) internal pure returns (uint256) {
require(_upperBound > 0, "UniformRand/min-bound");
uint256 min = uint(-int(_upperBound)) % _upperBound;
uint256 random = _entropy;
while (true) {
if (random >= min) {
break;
}
random = uint256(keccak256(abi.encodePacked(random)));
}
return random % _upperBound;
}
}
{
"compilationTarget": {
"src/KingOfTheDegens.sol": "KingOfTheDegens"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/",
":@prb/math/=lib/prb-math/",
":@prb/test/=lib/prb-math/node_modules/@prb/test/",
":@uniswap/swap-router-contracts/contracts/=lib/swap-router-contracts/contracts/",
":@uniswap/v3-core/contracts/=lib/v3-core/contracts/",
":@uniswap/v3-periphery/contracts/=lib/v3-periphery/contracts/",
":ds-test/=lib/solmate/lib/ds-test/src/",
":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":prb-math/=lib/prb-math/src/",
":solmate/=lib/solmate/src/",
":superfluid-contracts/=lib/superfluid-protocol-monorepo/packages/ethereum-contracts/contracts/",
":superfluid-protocol-monorepo/=lib/superfluid-protocol-monorepo/packages/solidity-semantic-money/src/",
":swap-router-contracts/=lib/swap-router-contracts/contracts/",
":trustus/=lib/trustus/src/",
":v3-core/=lib/v3-core/",
":v3-periphery/=lib/v3-periphery/contracts/",
":weird-erc20/=lib/trustus/lib/solmate/lib/weird-erc20/src/"
]
}
[{"inputs":[{"internalType":"uint256","name":"_gameDurationBlocks","type":"uint256"},{"internalType":"uint256","name":"_minPlayAmount","type":"uint256"},{"internalType":"uint256","name":"_protocolFee","type":"uint256"},{"internalType":"uint256","name":"_stormFrequencyBlocks","type":"uint256"},{"internalType":"uint256","name":"_redeemAfterGameEndedBlocks","type":"uint256"},{"internalType":"uint256[4]","name":"_courtBps","type":"uint256[4]"},{"internalType":"uint8[4]","name":"_courtRolePercentages","type":"uint8[4]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"enum KingOfTheDegens.CourtRole","name":"courtRole","type":"uint8"}],"name":"AlreadyCourtMember","type":"error"},{"inputs":[{"internalType":"uint256","name":"length1","type":"uint256"},{"internalType":"uint256","name":"length2","type":"uint256"}],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"courtRole","type":"uint8"}],"name":"BadCourtRole","type":"error"},{"inputs":[{"internalType":"uint8","name":"percentageTotal","type":"uint8"}],"name":"BadCourtRolePercentages","type":"error"},{"inputs":[],"name":"BadZeroAddress","type":"error"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"enum KingOfTheDegens.CourtRole","name":"courtRole","type":"uint8"},{"internalType":"enum KingOfTheDegens.CourtRole","name":"expectedCourtRole","type":"uint8"}],"name":"CourtRoleMismatch","type":"error"},{"inputs":[],"name":"GameIsActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"gameStartBlock","type":"uint256"},{"internalType":"uint256","name":"gameEndBlock","type":"uint256"},{"internalType":"uint256","name":"currentBlock","type":"uint256"}],"name":"GameNotActive","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"valueSent","type":"uint256"}],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"RedeemEnded","type":"error"},{"inputs":[{"internalType":"uint256","name":"redeemEndedBlock","type":"uint256"}],"name":"RedeemStillActive","type":"error"},{"inputs":[{"internalType":"uint256","name":"nextBlockAllowed","type":"uint256"},{"internalType":"uint256","name":"currentBlockNumber","type":"uint256"}],"name":"TooFrequentStorms","type":"error"},{"inputs":[],"name":"Trustus__InvalidPacket","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"accountAddress","type":"address"},{"indexed":true,"internalType":"uint256","name":"amountRedeemed","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"pointsRedeemed","type":"uint256"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"accountAddress","type":"address"},{"indexed":true,"internalType":"uint8","name":"courtRole","type":"uint8"},{"indexed":true,"internalType":"address","name":"outAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"fid","type":"uint256"}],"name":"StormTheCastle","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRUSTUS_STORM","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"uint256","name":"endBlockNumber","type":"uint256"},{"internalType":"enum KingOfTheDegens.CourtRole","name":"courtRole","type":"uint8"}],"name":"calculatePointsEarned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collectProtocolFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"points","type":"uint256"}],"name":"convertPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"","type":"uint8"}],"name":"courtBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"courtRoles","outputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"degenToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"degenAmountWei","type":"uint256"}],"name":"depositDegenToGameAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"accountAddress","type":"address"},{"internalType":"uint256","name":"_randomSeed","type":"uint256"}],"name":"determineCourtRole","outputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameDurationBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameEndBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameLastBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gameStartBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCourtAddresses","outputs":[{"internalType":"address[10]","name":"","type":"address[10]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCourtMemberPoints","outputs":[{"internalType":"uint256[10]","name":"","type":"uint256[10]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getCourtRoleFromAddressesIndex","outputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"enum KingOfTheDegens.CourtRole","name":"courtRole","type":"uint8"}],"name":"getPointsPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_storms","type":"uint256"},{"internalType":"address[]","name":"_accountAddresses","type":"address[]"},{"internalType":"uint256[]","name":"_points","type":"uint256[]"},{"internalType":"uint256[]","name":"_stormBlocks","type":"uint256[]"}],"name":"initGameState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isGameActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isGameEnded","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isGameStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"king","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"knights","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lords","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minPlayAmount","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":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pointsBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolRedeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redeemAfterGameEndedBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"roleRanges","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"roleStartBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[4]","name":"_courtBps","type":"uint256[4]"}],"name":"setCourtBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[4]","name":"_courtRolePercentages","type":"uint8[4]"}],"name":"setCourtRolePercentages","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"trustedAddress","type":"address"},{"internalType":"bool","name":"isTrusted","type":"bool"}],"name":"setIsTrusted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blocks","type":"uint256"}],"name":"setStormFrequency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[1]","name":"_king","type":"address[1]"},{"internalType":"address[2]","name":"_lords","type":"address[2]"},{"internalType":"address[3]","name":"_knights","type":"address[3]"},{"internalType":"address[4]","name":"_townsfolk","type":"address[4]"},{"internalType":"uint256","name":"_startBlock","type":"uint256"}],"name":"startGame","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stormBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stormFrequencyBlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"bytes32","name":"request","type":"bytes32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct Trustus.TrustusPacket","name":"packet","type":"tuple"}],"name":"stormTheCastle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"storms","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPointsPerBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"townsfolk","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]