Skip to content

Instantly share code, notes, and snippets.

@wadealexc
Last active October 25, 2018 11:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wadealexc/596ca9cbfa1c4572454965d539e881e2 to your computer and use it in GitHub Desktop.
Save wadealexc/596ca9cbfa1c4572454965d539e881e2 to your computer and use it in GitHub Desktop.
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;
}
}
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