-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: add 3 new ethernaut challenges
- Loading branch information
Showing
4 changed files
with
339 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import '@openzeppelin-08/token/ERC20/IERC20.sol'; | ||
import '@openzeppelin-08/token/ERC20/ERC20.sol'; | ||
import '@openzeppelin-08/access/Ownable.sol'; | ||
|
||
contract DexTwo is Ownable { | ||
address public token1; | ||
address public token2; | ||
|
||
constructor() Ownable(msg.sender) {} | ||
|
||
function setTokens(address _token1, address _token2) public onlyOwner { | ||
token1 = _token1; | ||
token2 = _token2; | ||
} | ||
|
||
function add_liquidity( | ||
address token_address, | ||
uint256 amount | ||
) public onlyOwner { | ||
IERC20(token_address).transferFrom(msg.sender, address(this), amount); | ||
} | ||
|
||
function swap(address from, address to, uint256 amount) public { | ||
require(IERC20(from).balanceOf(msg.sender) >= amount, 'Not enough to swap'); | ||
uint256 swapAmount = getSwapAmount(from, to, amount); | ||
IERC20(from).transferFrom(msg.sender, address(this), amount); | ||
IERC20(to).approve(address(this), swapAmount); | ||
IERC20(to).transferFrom(address(this), msg.sender, swapAmount); | ||
} | ||
|
||
function getSwapAmount( | ||
address from, | ||
address to, | ||
uint256 amount | ||
) public view returns (uint256) { | ||
return ((amount * IERC20(to).balanceOf(address(this))) / | ||
IERC20(from).balanceOf(address(this))); | ||
} | ||
|
||
function approve(address spender, uint256 amount) public { | ||
SwappableTokenTwo(token1).approve(msg.sender, spender, amount); | ||
SwappableTokenTwo(token2).approve(msg.sender, spender, amount); | ||
} | ||
|
||
function balanceOf( | ||
address token, | ||
address account | ||
) public view returns (uint256) { | ||
return IERC20(token).balanceOf(account); | ||
} | ||
} | ||
|
||
contract SwappableTokenTwo is ERC20 { | ||
address private _dex; | ||
|
||
constructor( | ||
address dexInstance, | ||
string memory name, | ||
string memory symbol, | ||
uint256 initialSupply | ||
) ERC20(name, symbol) { | ||
_mint(msg.sender, initialSupply); | ||
_dex = dexInstance; | ||
} | ||
|
||
function approve(address owner, address spender, uint256 amount) public { | ||
require(owner != _dex, 'InvalidApprover'); | ||
super._approve(owner, spender, amount); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import '@openzeppelin-08/access/Ownable.sol'; | ||
import '@openzeppelin-08/token/ERC20/ERC20.sol'; | ||
|
||
interface DelegateERC20 { | ||
function delegateTransfer( | ||
address to, | ||
uint256 value, | ||
address origSender | ||
) external returns (bool); | ||
} | ||
|
||
interface IDetectionBot { | ||
function handleTransaction(address user, bytes calldata msgData) external; | ||
} | ||
|
||
interface IForta { | ||
function setDetectionBot(address detectionBotAddress) external; | ||
function notify(address user, bytes calldata msgData) external; | ||
function raiseAlert(address user) external; | ||
} | ||
|
||
contract Forta is IForta { | ||
mapping(address => IDetectionBot) public usersDetectionBots; | ||
mapping(address => uint256) public botRaisedAlerts; | ||
|
||
function setDetectionBot(address detectionBotAddress) external override { | ||
usersDetectionBots[msg.sender] = IDetectionBot(detectionBotAddress); | ||
} | ||
|
||
function notify(address user, bytes calldata msgData) external override { | ||
if (address(usersDetectionBots[user]) == address(0)) return; | ||
try usersDetectionBots[user].handleTransaction(user, msgData) { | ||
return; | ||
} catch {} | ||
} | ||
|
||
function raiseAlert(address user) external override { | ||
if (address(usersDetectionBots[user]) != msg.sender) return; | ||
botRaisedAlerts[msg.sender] += 1; | ||
} | ||
} | ||
|
||
contract CryptoVault { | ||
address public sweptTokensRecipient; | ||
IERC20 public underlying; | ||
|
||
constructor(address recipient) { | ||
sweptTokensRecipient = recipient; | ||
} | ||
|
||
function setUnderlying(address latestToken) public { | ||
require(address(underlying) == address(0), 'Already set'); | ||
underlying = IERC20(latestToken); | ||
} | ||
|
||
/* | ||
... | ||
*/ | ||
|
||
function sweepToken(IERC20 token) public { | ||
require(token != underlying, "Can't transfer underlying token"); | ||
token.transfer(sweptTokensRecipient, token.balanceOf(address(this))); | ||
} | ||
} | ||
|
||
contract LegacyToken is ERC20('LegacyToken', 'LGT'), Ownable(msg.sender) { | ||
DelegateERC20 public delegate; | ||
|
||
function mint(address to, uint256 amount) public onlyOwner { | ||
_mint(to, amount); | ||
} | ||
|
||
function delegateToNewContract(DelegateERC20 newContract) public onlyOwner { | ||
delegate = newContract; | ||
} | ||
|
||
function transfer(address to, uint256 value) public override returns (bool) { | ||
if (address(delegate) == address(0)) { | ||
return super.transfer(to, value); | ||
} else { | ||
return delegate.delegateTransfer(to, value, msg.sender); | ||
} | ||
} | ||
} | ||
|
||
contract DoubleEntryPoint is | ||
ERC20('DoubleEntryPointToken', 'DET'), | ||
DelegateERC20, | ||
Ownable(msg.sender) | ||
{ | ||
address public cryptoVault; | ||
address public player; | ||
address public delegatedFrom; | ||
Forta public forta; | ||
|
||
constructor( | ||
address legacyToken, | ||
address vaultAddress, | ||
address fortaAddress, | ||
address playerAddress | ||
) { | ||
delegatedFrom = legacyToken; | ||
forta = Forta(fortaAddress); | ||
player = playerAddress; | ||
cryptoVault = vaultAddress; | ||
_mint(cryptoVault, 100 ether); | ||
} | ||
|
||
modifier onlyDelegateFrom() { | ||
require(msg.sender == delegatedFrom, 'Not legacy contract'); | ||
_; | ||
} | ||
|
||
modifier fortaNotify() { | ||
address detectionBot = address(forta.usersDetectionBots(player)); | ||
|
||
// Cache old number of bot alerts | ||
uint256 previousValue = forta.botRaisedAlerts(detectionBot); | ||
|
||
// Notify Forta | ||
forta.notify(player, msg.data); | ||
|
||
// Continue execution | ||
_; | ||
|
||
// Check if alarms have been raised | ||
if (forta.botRaisedAlerts(detectionBot) > previousValue) | ||
revert('Alert has been triggered, reverting'); | ||
} | ||
|
||
function delegateTransfer( | ||
address to, | ||
uint256 value, | ||
address origSender | ||
) public override onlyDelegateFrom fortaNotify returns (bool) { | ||
_transfer(origSender, to, value); | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity <0.7.0; | ||
|
||
import '@openzeppelin-06/utils/Address.sol'; | ||
import '@openzeppelin-06/proxy/Initializable.sol'; | ||
|
||
contract Motorbike { | ||
// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 | ||
bytes32 internal constant _IMPLEMENTATION_SLOT = | ||
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | ||
|
||
struct AddressSlot { | ||
address value; | ||
} | ||
|
||
// Initializes the upgradeable proxy with an initial implementation specified by `_logic`. | ||
constructor(address _logic) public { | ||
require( | ||
Address.isContract(_logic), | ||
'ERC1967: new implementation is not a contract' | ||
); | ||
_getAddressSlot(_IMPLEMENTATION_SLOT).value = _logic; | ||
(bool success, ) = _logic.delegatecall( | ||
abi.encodeWithSignature('initialize()') | ||
); | ||
require(success, 'Call failed'); | ||
} | ||
|
||
// Delegates the current call to `implementation`. | ||
function _delegate(address implementation) internal virtual { | ||
// solhint-disable-next-line no-inline-assembly | ||
assembly { | ||
calldatacopy(0, 0, calldatasize()) | ||
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) | ||
returndatacopy(0, 0, returndatasize()) | ||
switch result | ||
case 0 { | ||
revert(0, returndatasize()) | ||
} | ||
default { | ||
return(0, returndatasize()) | ||
} | ||
} | ||
} | ||
|
||
// Fallback function that delegates calls to the address returned by `_implementation()`. | ||
// Will run if no other function in the contract matches the call data | ||
fallback() external payable virtual { | ||
_delegate(_getAddressSlot(_IMPLEMENTATION_SLOT).value); | ||
} | ||
|
||
// Returns an `AddressSlot` with member `value` located at `slot`. | ||
function _getAddressSlot( | ||
bytes32 slot | ||
) internal pure returns (AddressSlot storage r) { | ||
assembly { | ||
r_slot := slot | ||
} | ||
} | ||
} | ||
|
||
contract Engine is Initializable { | ||
// keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1 | ||
bytes32 internal constant _IMPLEMENTATION_SLOT = | ||
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | ||
|
||
address public upgrader; | ||
uint256 public horsePower; | ||
|
||
struct AddressSlot { | ||
address value; | ||
} | ||
|
||
function initialize() external initializer { | ||
horsePower = 1000; | ||
upgrader = msg.sender; | ||
} | ||
|
||
// Upgrade the implementation of the proxy to `newImplementation` | ||
// subsequently execute the function call | ||
function upgradeToAndCall( | ||
address newImplementation, | ||
bytes memory data | ||
) external payable { | ||
_authorizeUpgrade(); | ||
_upgradeToAndCall(newImplementation, data); | ||
} | ||
|
||
// Restrict to upgrader role | ||
function _authorizeUpgrade() internal view { | ||
require(msg.sender == upgrader, "Can't upgrade"); | ||
} | ||
|
||
// Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. | ||
function _upgradeToAndCall( | ||
address newImplementation, | ||
bytes memory data | ||
) internal { | ||
// Initial upgrade and setup call | ||
_setImplementation(newImplementation); | ||
if (data.length > 0) { | ||
(bool success, ) = newImplementation.delegatecall(data); | ||
require(success, 'Call failed'); | ||
} | ||
} | ||
|
||
// Stores a new address in the EIP1967 implementation slot. | ||
function _setImplementation(address newImplementation) private { | ||
require( | ||
Address.isContract(newImplementation), | ||
'ERC1967: new implementation is not a contract' | ||
); | ||
|
||
AddressSlot storage r; | ||
assembly { | ||
r_slot := _IMPLEMENTATION_SLOT | ||
} | ||
r.value = newImplementation; | ||
} | ||
} |