Last active
October 25, 2018 11:53
-
-
Save wadealexc/596ca9cbfa1c4572454965d539e881e2 to your computer and use it in GitHub Desktop.
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
pragma solidity ^0.4.24; | |
library SafeMath { | |
function sub(uint256 a, uint256 b) internal pure returns (uint256) { | |
require(b <= a); | |
uint256 c = a - b; | |
return c; | |
} | |
function add(uint256 a, uint256 b) internal pure returns (uint256) { | |
uint256 c = a + b; | |
require(c >= a); | |
return c; | |
} | |
} | |
library UAuthLib { | |
// Check that the calldata is 32 bytes over the _norm, then read the last word of calldata | |
// If the first byte is '2', the sender is a user. Return the address. Otherwise, return 0 | |
function checkIfUser(uint _norm) internal pure returns (address user) { | |
require(_norm < _norm + 32 && msg.data.length == _norm + 32); | |
assembly { | |
let cd := calldataload(sub(calldatasize, 32)) | |
let stat := byte(0, cd) | |
if eq(stat, 2) { user := cd } // Unclean bits | |
} | |
} | |
} | |
contract ERC20Target { | |
using SafeMath for uint; | |
string public constant name = "Token Name"; | |
string public constant symbol = "SYMB"; | |
uint8 public constant decimals = 18; | |
address uauth; | |
uint supply; | |
mapping (address => uint) balances; | |
event Transfer(address indexed owner, address indexed spender, uint amount); | |
/** | |
* @dev ERC20 transfer with metadata | |
* @param _dest Destination address | |
* @param _amt Amount of tokens to send | |
* @param _data Metadata - whether the sender is a contract, user, or pending verification. | |
* This contract is user-only! | |
*/ | |
function transfer(address _dest, uint _amt) external returns (bool) { | |
// Require our sender to be the uauth contract, and get their address | |
require(msg.sender == uauth); | |
address sender = UAuthLib.checkIfUser(68); // '68' is the expected cdsize of this func | |
require(sender != 0); | |
// You know the deal | |
balances[sender] = balances[sender].sub(_amt); | |
balances[_dest] = balances[_dest].add(_amt); | |
emit Transfer(sender, _dest, _amt); | |
return true; | |
} | |
function balanceOf(address _a) external view returns (uint) { | |
return balances[_a]; | |
} | |
function totalSupply() external view returns (uint) { | |
return supply; | |
} | |
} |
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
pragma solidity ^0.4.24; | |
contract UserAuth { | |
struct User { | |
Status s; | |
uint64 time; | |
} | |
enum Status { UNKNOWN, PENDING, USER, CONTRACT } | |
mapping (address => User) status; | |
/** | |
* @dev Fallback function. Checks if the sender is a user or contract, | |
* and appends the information to the calldata, adding 32 bytes. | |
* The target is then called and passed the modified calldata. This | |
* function then gets the returndata and status of the call and mimics it. | |
* | |
* Calldata should be sent to the fallback in the following format -- | |
* [address, 20 bytes] - Target application address | |
* [bytes, variable size] - Target application calldata | |
*/ | |
function () external payable { | |
// Need to send at least the target address | |
require(msg.data.length >= 20); | |
// Reference the return as memory so we don't sload twice TODO check if this happens :/ | |
User memory user; | |
check(user, msg.sender); | |
assembly { | |
// First 20 bytes - address | |
let target := calldataload(0) | |
target := div(target, exp(2, 96)) // Shift right 20 bytes | |
// Copy calldata minus destination address to memory | |
let cd_size := sub(calldatasize, 20) | |
calldatacopy(0, 20, cd_size) | |
// Append user status enum and verification time, packed into a single slot | |
let packed := or( | |
mload(add(32, user)), | |
mul(mload(user), exp(2, 248)) | |
) | |
mstore(cd_size, packed) | |
// Execute target application | |
let ret := call(gas, target, callvalue, 0, add(32, cd_size), 0, 0) | |
// Copy returndata to memory | |
returndatacopy(0, 0, returndatasize) | |
// If the external call failed, revert. Otherwise, return | |
// In both cases, revert/return the returndata | |
switch ret | |
case 0 { revert(0, returndatasize) } | |
default { return(0, returndatasize) } | |
} | |
} | |
/** | |
* @dev Allows a user to send an initial transaction to shift their status | |
*/ | |
function __verify__() external { | |
User memory u; | |
check(u, msg.sender); | |
} | |
/** | |
* @dev Get the status of _a. If needed, update the status | |
* @param _a The address to query | |
* @return cur The status of _a after the check was performed | |
*/ | |
function check(User memory _u, address _a) internal { | |
_u = status[_a]; | |
if (_u.s == Status.UNKNOWN) { // _a has not interacted with this contract before | |
_u.s = Status.PENDING; | |
_u.time = uint64(now); | |
status[_a] = _u; | |
} else if (_u.s == Status.PENDING) { // This is _a's second interaction with this contract | |
// Transactions must be in separate blocks | |
// Otherwise, a constructor could send 2 calls consecutively and be marked "User" | |
require(_u.time != now); | |
if (extcode(_a) == 0) { // No code in 2 separate blocks - User | |
_u.s = Status.USER; | |
_u.time = uint64(now); | |
} else { // Code found - Contract | |
_u.s = Status.CONTRACT; | |
_u.time = uint64(now); | |
} | |
status[_a] = _u; | |
} | |
} | |
/** | |
* @dev Get the extcodesize of _a | |
* @param _a The address to query | |
* @return size The size of the code at _a | |
*/ | |
function extcode(address _a) internal view returns (uint size) { | |
assembly { size := extcodesize(_a) } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment