Transaction Hash:
Block:
21972752 at Mar-04-2025 09:52:59 AM +UTC
Transaction Fee:
0.000072972 ETH
$0.14
Gas Used:
72,972 Gas / 1 Gwei
Emitted Events:
470 |
AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000009891883707e4bf3ad1c584a2a4bdca2851e20d06, 0x000000000000000000000000bd2bd73e853d966afbba2c2735e571141a7eb4a2, 000000000000000000000000000000000000000000000000003baf82d03a0000 )
|
471 |
AdminUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000009891883707e4bf3ad1c584a2a4bdca2851e20d06, 0x00000000000000000000000038699d04656ff537ef8671b6b595402ebdbdf6f4, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x45804880...E6EcbAf78 | |||||
0x95222290...5CC4BAfe5
Miner
| (beaverbuild) | 10.399457054345000926 Eth | 10.399485444113568334 Eth | 0.000028389768567408 | |
0x98918837...851E20D06 |
0.290971645 Eth
Nonce: 39
|
0.290898673 Eth
Nonce: 40
| 0.000072972 |
Execution Trace
AdminUpgradeabilityProxy.a9059cbb( )

-
PAXGImplementation.transfer( _to=0xbD2BD73E853D966afbbA2c2735e571141a7eB4a2, _value=16800000000000000 ) => ( True )
transfer[PAXGImplementation (ln:287)]
File 1 of 2: AdminUpgradeabilityProxy
File 2 of 2: PAXGImplementation
// File: contracts/zeppelin/Proxy.sol pragma solidity 0.4.24; /** * @title Proxy * @dev Implements delegation of calls to other contracts, with proper * forwarding of return values and bubbling of failures. * It defines a fallback function that delegates all calls to the address * returned by the abstract _implementation() internal function. */ contract Proxy { /** * @dev Fallback function. * Implemented entirely in `_fallback`. */ function () payable external { _fallback(); } /** * @return The Address of the implementation. */ function _implementation() internal view returns (address); /** * @dev Delegates execution to an implementation contract. * This is a low level function that doesn't return to its internal call site. * It will return to the external caller whatever the implementation returns. * @param implementation Address to delegate. */ function _delegate(address implementation) internal { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize) } default { return(0, returndatasize) } } } /** * @dev Function that is run as the first thing in the fallback function. * Can be redefined in derived contracts to add functionality. * Redefinitions must call super._willFallback(). */ function _willFallback() internal { } /** * @dev fallback implementation. * Extracted to enable manual triggering. */ function _fallback() internal { _willFallback(); _delegate(_implementation()); } } // File: contracts/zeppelin/AddressUtils.sol pragma solidity 0.4.24; /** * Utility library of inline functions on addresses */ library AddressUtils { /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract, * as the code is not actually created until after the constructor finishes. * @param addr address to check * @return whether the target address is a contract */ function isContract(address addr) internal view returns (bool) { uint256 size; // XXX Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address. // See https://ethereum.stackexchange.com/a/14016/36603 // for more details about how this works. // TODO Check this again before the Serenity release, because all addresses will be // contracts then. // solium-disable-next-line security/no-inline-assembly assembly { size := extcodesize(addr) } return size > 0; } } // File: contracts/zeppelin/UpgradeabilityProxy.sol pragma solidity 0.4.24; /** * @title UpgradeabilityProxy * @dev This contract implements a proxy that allows to change the * implementation address to which it will delegate. * Such a change is called an implementation upgrade. */ contract UpgradeabilityProxy is Proxy { /** * @dev Emitted when the implementation is upgraded. * @param implementation Address of the new implementation. */ event Upgraded(address implementation); /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is * validated in the constructor. */ bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; /** * @dev Contract constructor. * @param _implementation Address of the initial implementation. */ constructor(address _implementation) public { assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); _setImplementation(_implementation); } /** * @dev Returns the current implementation. * @return Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Upgrades the proxy to a new implementation. * @param newImplementation Address of the new implementation. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Sets the implementation address of the proxy. * @param newImplementation Address of the new implementation. */ function _setImplementation(address newImplementation) private { require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, newImplementation) } } } // File: contracts/zeppelin/AdminUpgradeabilityProxy.sol pragma solidity 0.4.24; /** * @title AdminUpgradeabilityProxy * @dev This contract combines an upgradeability proxy with an authorization * mechanism for administrative tasks. * All external functions in this contract must be guarded by the * `ifAdmin` modifier. See ethereum/solidity#3864 for a Solidity * feature proposal that would enable this to be done automatically. */ contract AdminUpgradeabilityProxy is UpgradeabilityProxy { /** * @dev Emitted when the administration has been transferred. * @param previousAdmin Address of the previous admin. * @param newAdmin Address of the new admin. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "org.zeppelinos.proxy.admin", and is * validated in the constructor. */ bytes32 private constant ADMIN_SLOT = 0x10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b; /** * @dev Modifier to check whether the `msg.sender` is the admin. * If it is, it will run the function. Otherwise, it will delegate the call * to the implementation. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * Contract constructor. * It sets the `msg.sender` as the proxy administrator. * @param _implementation address of the initial implementation. */ constructor(address _implementation) UpgradeabilityProxy(_implementation) public { assert(ADMIN_SLOT == keccak256("org.zeppelinos.proxy.admin")); _setAdmin(msg.sender); } /** * @return The address of the proxy admin. */ function admin() external view ifAdmin returns (address) { return _admin(); } /** * @return The address of the implementation. */ function implementation() external view ifAdmin returns (address) { return _implementation(); } /** * @dev Changes the admin of the proxy. * Only the current admin can call this function. * @param newAdmin Address to transfer proxy administration to. */ function changeAdmin(address newAdmin) external ifAdmin { require(newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); emit AdminChanged(_admin(), newAdmin); _setAdmin(newAdmin); } /** * @dev Upgrade the backing implementation of the proxy. * Only the admin can call this function. * @param newImplementation Address of the new implementation. */ function upgradeTo(address newImplementation) external ifAdmin { _upgradeTo(newImplementation); } /** * @dev Upgrade the backing implementation of the proxy and call a function * on the new implementation. * This is useful to initialize the proxied contract. * @param newImplementation Address of the new implementation. * @param data Data to send as msg.data in the low level call. * It should include the signature and the parameters of the function to be * called, as described in * https://solidity.readthedocs.io/en/develop/abi-spec.html#function-selector-and-argument-encoding. */ function upgradeToAndCall(address newImplementation, bytes data) payable external ifAdmin { _upgradeTo(newImplementation); require(address(this).call.value(msg.value)(data)); } /** * @return The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param newAdmin Address of the new proxy admin. */ function _setAdmin(address newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, newAdmin) } } /** * @dev Only fall back when the sender is not the admin. */ function _willFallback() internal { require(msg.sender != _admin(), "Cannot call fallback function from the proxy admin"); super._willFallback(); } }
File 2 of 2: PAXGImplementation
// File: contracts/zeppelin/SafeMath.sol pragma solidity 0.4.24; /** * @title SafeMath * @dev Math operations with safety checks that throw on error */ library SafeMath { /** * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a); uint256 c = a - b; return c; } /** * @dev Adds two numbers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a); return c; } /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } } // File: contracts/PAXGImplementation.sol pragma solidity 0.4.24; pragma experimental "v0.5.0"; /** * @title PAXGImplementation * @dev this contract is a Pausable ERC20 token with Burn and Mint * controlled by a central SupplyController. By implementing PaxosImplementation * this contract also includes external methods for setting * a new implementation contract for the Proxy. * NOTE: The storage defined here will actually be held in the Proxy * contract and all calls to this contract should be made through * the proxy, including admin actions done as owner or supplyController. * Any call to transfer against this contract should fail * with insufficient funds since no tokens will be issued there. */ contract PAXGImplementation { /** * MATH */ using SafeMath for uint256; /** * DATA */ // INITIALIZATION DATA bool private initialized = false; // ERC20 BASIC DATA mapping(address => uint256) internal balances; uint256 internal totalSupply_; string public constant name = "Paxos Gold"; // solium-disable-line string public constant symbol = "PAXG"; // solium-disable-line uppercase uint8 public constant decimals = 18; // solium-disable-line uppercase // ERC20 DATA mapping(address => mapping(address => uint256)) internal allowed; // OWNER DATA address public owner; address public proposedOwner; // PAUSABILITY DATA bool public paused = false; // ASSET PROTECTION DATA address public assetProtectionRole; mapping(address => bool) internal frozen; // SUPPLY CONTROL DATA address public supplyController; // DELEGATED TRANSFER DATA address public betaDelegateWhitelister; mapping(address => bool) internal betaDelegateWhitelist; mapping(address => uint256) internal nextSeqs; // EIP191 header for EIP712 prefix string constant internal EIP191_HEADER = "\x19\x01"; // Hash of the EIP712 Domain Separator Schema bytes32 constant internal EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH = keccak256( "EIP712Domain(string name,address verifyingContract)" ); bytes32 constant internal EIP712_DELEGATED_TRANSFER_SCHEMA_HASH = keccak256( "BetaDelegatedTransfer(address to,uint256 value,uint256 serviceFee,uint256 seq,uint256 deadline)" ); // Hash of the EIP712 Domain Separator data // solhint-disable-next-line var-name-mixedcase bytes32 public EIP712_DOMAIN_HASH; // FEE CONTROLLER DATA // fee decimals is only set for informational purposes. // 1 feeRate = .000001 oz of gold uint8 public constant feeDecimals = 6; // feeRate is measured in 100th of a basis point (parts per 1,000,000) // ex: a fee rate of 200 = 0.02% of an oz of gold uint256 public constant feeParts = 1000000; uint256 public feeRate; address public feeController; address public feeRecipient; /** * EVENTS */ // ERC20 BASIC EVENTS event Transfer(address indexed from, address indexed to, uint256 value); // ERC20 EVENTS event Approval( address indexed owner, address indexed spender, uint256 value ); // OWNABLE EVENTS event OwnershipTransferProposed( address indexed currentOwner, address indexed proposedOwner ); event OwnershipTransferDisregarded( address indexed oldProposedOwner ); event OwnershipTransferred( address indexed oldOwner, address indexed newOwner ); // PAUSABLE EVENTS event Pause(); event Unpause(); // ASSET PROTECTION EVENTS event AddressFrozen(address indexed addr); event AddressUnfrozen(address indexed addr); event FrozenAddressWiped(address indexed addr); event AssetProtectionRoleSet ( address indexed oldAssetProtectionRole, address indexed newAssetProtectionRole ); // SUPPLY CONTROL EVENTS event SupplyIncreased(address indexed to, uint256 value); event SupplyDecreased(address indexed from, uint256 value); event SupplyControllerSet( address indexed oldSupplyController, address indexed newSupplyController ); // DELEGATED TRANSFER EVENTS event BetaDelegatedTransfer( address indexed from, address indexed to, uint256 value, uint256 seq, uint256 serviceFee ); event BetaDelegateWhitelisterSet( address indexed oldWhitelister, address indexed newWhitelister ); event BetaDelegateWhitelisted(address indexed newDelegate); event BetaDelegateUnwhitelisted(address indexed oldDelegate); // FEE CONTROLLER EVENTS event FeeCollected(address indexed from, address indexed to, uint256 value); event FeeRateSet( uint256 indexed oldFeeRate, uint256 indexed newFeeRate ); event FeeControllerSet( address indexed oldFeeController, address indexed newFeeController ); event FeeRecipientSet( address indexed oldFeeRecipient, address indexed newFeeRecipient ); /** * FUNCTIONALITY */ // INITIALIZATION FUNCTIONALITY /** * @dev sets 0 initial tokens, the owner, the supplyController, * the fee controller and fee recipient. * this serves as the constructor for the proxy but compiles to the * memory model of the Implementation contract. */ function initialize() public { require(!initialized, "already initialized"); owner = msg.sender; proposedOwner = address(0); assetProtectionRole = address(0); totalSupply_ = 0; supplyController = msg.sender; feeRate = 0; feeController = msg.sender; feeRecipient = msg.sender; initializeDomainSeparator(); initialized = true; } /** * The constructor is used here to ensure that the implementation * contract is initialized. An uncontrolled implementation * contract might lead to misleading state * for users who accidentally interact with it. */ constructor() public { initialize(); pause(); } /** * @dev To be called when upgrading the contract using upgradeAndCall to add delegated transfers */ function initializeDomainSeparator() public { // hash the name context with the contract address EIP712_DOMAIN_HASH = keccak256(abi.encodePacked(// solium-disable-line EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH, keccak256(bytes(name)), bytes32(address(this)) )); } // ERC20 BASIC FUNCTIONALITY /** * @dev Total number of tokens in existence */ function totalSupply() public view returns (uint256) { return totalSupply_; } /** * @dev Transfer token to a specified address from msg.sender * Transfer additionally sends the fee to the fee controller * Note: the use of Safemath ensures that _value is nonnegative. * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) { require(_to != address(0), "cannot transfer to address zero"); require(!frozen[_to] && !frozen[msg.sender], "address frozen"); require(_value <= balances[msg.sender], "insufficient funds"); _transfer(msg.sender, _to, _value); return true; } /** * @dev Gets the balance of the specified address. * @param _addr The address to query the the balance of. * @return An uint256 representing the amount owned by the passed address. */ function balanceOf(address _addr) public view returns (uint256) { return balances[_addr]; } // ERC20 FUNCTIONALITY /** * @dev Transfer tokens from one address to another * @param _from address The address which you want to send tokens from * @param _to address The address which you want to transfer to * @param _value uint256 the amount of tokens to be transferred */ function transferFrom( address _from, address _to, uint256 _value ) public whenNotPaused returns (bool) { require(_to != address(0), "cannot transfer to address zero"); require(!frozen[_to] && !frozen[_from] && !frozen[msg.sender], "address frozen"); require(_value <= balances[_from], "insufficient funds"); require(_value <= allowed[_from][msg.sender], "insufficient allowance"); allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); _transfer(_from, _to, _value); return true; } /** * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender. * Beware that changing an allowance with this method brings the risk that someone may use both the old * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * @param _spender The address which will spend the funds. * @param _value The amount of tokens to be spent. */ function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) { require(!frozen[_spender] && !frozen[msg.sender], "address frozen"); allowed[msg.sender][_spender] = _value; emit Approval(msg.sender, _spender, _value); return true; } /** * @dev Function to check the amount of tokens that an owner allowed to a spender. * @param _owner address The address which owns the funds. * @param _spender address The address which will spend the funds. * @return A uint256 specifying the amount of tokens still available for the spender. */ function allowance( address _owner, address _spender ) public view returns (uint256) { return allowed[_owner][_spender]; } function _transfer(address _from, address _to, uint256 _value) internal returns (uint256) { uint256 _fee = getFeeFor(_value); uint256 _principle = _value.sub(_fee); balances[_from] = balances[_from].sub(_value); balances[_to] = balances[_to].add(_principle); emit Transfer(_from, _to, _principle); emit Transfer(_from, feeRecipient, _fee); if (_fee > 0) { balances[feeRecipient] = balances[feeRecipient].add(_fee); emit FeeCollected(_from, feeRecipient, _fee); } return _principle; } // OWNER FUNCTIONALITY /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(msg.sender == owner, "onlyOwner"); _; } /** * @dev Allows the current owner to begin transferring control of the contract to a proposedOwner * @param _proposedOwner The address to transfer ownership to. */ function proposeOwner(address _proposedOwner) public onlyOwner { require(_proposedOwner != address(0), "cannot transfer ownership to address zero"); require(msg.sender != _proposedOwner, "caller already is owner"); proposedOwner = _proposedOwner; emit OwnershipTransferProposed(owner, proposedOwner); } /** * @dev Allows the current owner or proposed owner to cancel transferring control of the contract to a proposedOwner */ function disregardProposeOwner() public { require(msg.sender == proposedOwner || msg.sender == owner, "only proposedOwner or owner"); require(proposedOwner != address(0), "can only disregard a proposed owner that was previously set"); address _oldProposedOwner = proposedOwner; proposedOwner = address(0); emit OwnershipTransferDisregarded(_oldProposedOwner); } /** * @dev Allows the proposed owner to complete transferring control of the contract to the proposedOwner. */ function claimOwnership() public { require(msg.sender == proposedOwner, "onlyProposedOwner"); address _oldOwner = owner; owner = proposedOwner; proposedOwner = address(0); emit OwnershipTransferred(_oldOwner, owner); } /** * @dev Reclaim all PAXG at the contract address. * This sends the PAXG tokens that this contract add holding to the owner. * Note: this is not affected by freeze constraints. */ function reclaimPAXG() external onlyOwner { uint256 _balance = balances[this]; balances[this] = 0; balances[owner] = balances[owner].add(_balance); emit Transfer(this, owner, _balance); } // PAUSABILITY FUNCTIONALITY /** * @dev Modifier to make a function callable only when the contract is not paused. */ modifier whenNotPaused() { require(!paused, "whenNotPaused"); _; } /** * @dev called by the owner to pause, triggers stopped state */ function pause() public onlyOwner { require(!paused, "already paused"); paused = true; emit Pause(); } /** * @dev called by the owner to unpause, returns to normal state */ function unpause() public onlyOwner { require(paused, "already unpaused"); paused = false; emit Unpause(); } // ASSET PROTECTION FUNCTIONALITY /** * @dev Sets a new asset protection role address. * @param _newAssetProtectionRole The new address allowed to freeze/unfreeze addresses and seize their tokens. */ function setAssetProtectionRole(address _newAssetProtectionRole) public { require(msg.sender == assetProtectionRole || msg.sender == owner, "only assetProtectionRole or Owner"); emit AssetProtectionRoleSet(assetProtectionRole, _newAssetProtectionRole); assetProtectionRole = _newAssetProtectionRole; } modifier onlyAssetProtectionRole() { require(msg.sender == assetProtectionRole, "onlyAssetProtectionRole"); _; } /** * @dev Freezes an address balance from being transferred. * @param _addr The new address to freeze. */ function freeze(address _addr) public onlyAssetProtectionRole { require(!frozen[_addr], "address already frozen"); frozen[_addr] = true; emit AddressFrozen(_addr); } /** * @dev Unfreezes an address balance allowing transfer. * @param _addr The new address to unfreeze. */ function unfreeze(address _addr) public onlyAssetProtectionRole { require(frozen[_addr], "address already unfrozen"); frozen[_addr] = false; emit AddressUnfrozen(_addr); } /** * @dev Wipes the balance of a frozen address, burning the tokens * and setting the approval to zero. * @param _addr The new frozen address to wipe. */ function wipeFrozenAddress(address _addr) public onlyAssetProtectionRole { require(frozen[_addr], "address is not frozen"); uint256 _balance = balances[_addr]; balances[_addr] = 0; totalSupply_ = totalSupply_.sub(_balance); emit FrozenAddressWiped(_addr); emit SupplyDecreased(_addr, _balance); emit Transfer(_addr, address(0), _balance); } /** * @dev Gets whether the address is currently frozen. * @param _addr The address to check if frozen. * @return A bool representing whether the given address is frozen. */ function isFrozen(address _addr) public view returns (bool) { return frozen[_addr]; } // SUPPLY CONTROL FUNCTIONALITY /** * @dev Sets a new supply controller address. * @param _newSupplyController The address allowed to burn/mint tokens to control supply. */ function setSupplyController(address _newSupplyController) public { require(msg.sender == supplyController || msg.sender == owner, "only SupplyController or Owner"); require(_newSupplyController != address(0), "cannot set supply controller to address zero"); emit SupplyControllerSet(supplyController, _newSupplyController); supplyController = _newSupplyController; } modifier onlySupplyController() { require(msg.sender == supplyController, "onlySupplyController"); _; } /** * @dev Increases the total supply by minting the specified number of tokens to the supply controller account. * @param _value The number of tokens to add. * @return A boolean that indicates if the operation was successful. */ function increaseSupply(uint256 _value) public onlySupplyController returns (bool success) { totalSupply_ = totalSupply_.add(_value); balances[supplyController] = balances[supplyController].add(_value); emit SupplyIncreased(supplyController, _value); emit Transfer(address(0), supplyController, _value); return true; } /** * @dev Decreases the total supply by burning the specified number of tokens from the supply controller account. * @param _value The number of tokens to remove. * @return A boolean that indicates if the operation was successful. */ function decreaseSupply(uint256 _value) public onlySupplyController returns (bool success) { require(_value <= balances[supplyController], "not enough supply"); balances[supplyController] = balances[supplyController].sub(_value); totalSupply_ = totalSupply_.sub(_value); emit SupplyDecreased(supplyController, _value); emit Transfer(supplyController, address(0), _value); return true; } // DELEGATED TRANSFER FUNCTIONALITY /** * @dev returns the next seq for a target address. * The transactor must submit nextSeqOf(transactor) in the next transaction for it to be valid. * Note: that the seq context is specific to this smart contract. * @param target The target address. * @return the seq. */ // function nextSeqOf(address target) public view returns (uint256) { return nextSeqs[target]; } /** * @dev Performs a transfer on behalf of the from address, identified by its signature on the delegatedTransfer msg. * Splits a signature byte array into r,s,v for convenience. * @param sig the signature of the delgatedTransfer msg. * @param to The address to transfer to. * @param value The amount to be transferred. * @param serviceFee an optional ERC20 service fee paid to the executor of betaDelegatedTransfer by the from address. * @param seq a sequencing number included by the from address specific to this contract to protect from replays. * @param deadline a block number after which the pre-signed transaction has expired. * @return A boolean that indicates if the operation was successful. */ function betaDelegatedTransfer( bytes sig, address to, uint256 value, uint256 serviceFee, uint256 seq, uint256 deadline ) public returns (bool) { require(sig.length == 65, "signature should have length 65"); bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(sig, 32)) s := mload(add(sig, 64)) v := byte(0, mload(add(sig, 96))) } require(_betaDelegatedTransfer(r, s, v, to, value, serviceFee, seq, deadline), "failed transfer"); return true; } /** * @dev Performs a transfer on behalf of the from address, identified by its signature on the betaDelegatedTransfer msg. * Note: both the delegate and transactor sign in the service fees. The transactor, however, * has no control over the gas price, and therefore no control over the transaction time. * Beta prefix chosen to avoid a name clash with an emerging standard in ERC865 or elsewhere. * Internal to the contract - see betaDelegatedTransfer and betaDelegatedTransferBatch. * @param r the r signature of the delgatedTransfer msg. * @param s the s signature of the delgatedTransfer msg. * @param v the v signature of the delgatedTransfer msg. * @param to The address to transfer to. * @param value The amount to be transferred. * @param serviceFee an optional ERC20 service fee paid to the delegate of betaDelegatedTransfer by the from address. * @param seq a sequencing number included by the from address specific to this contract to protect from replays. * @param deadline a block number after which the pre-signed transaction has expired. * @return A boolean that indicates if the operation was successful. */ function _betaDelegatedTransfer( bytes32 r, bytes32 s, uint8 v, address to, uint256 value, uint256 serviceFee, uint256 seq, uint256 deadline ) internal whenNotPaused returns (bool) { require(betaDelegateWhitelist[msg.sender], "Beta feature only accepts whitelisted delegates"); require(value > 0 || serviceFee > 0, "cannot transfer zero tokens with zero service fee"); require(block.number <= deadline, "transaction expired"); // prevent sig malleability from ecrecover() require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "signature incorrect"); require(v == 27 || v == 28, "signature incorrect"); // EIP712 scheme: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md bytes32 hash = keccak256(abi.encodePacked(EIP191_HEADER, EIP712_DOMAIN_HASH, keccak256(abi.encodePacked(// solium-disable-line EIP712_DELEGATED_TRANSFER_SCHEMA_HASH, bytes32(to), value, serviceFee, seq, deadline )))); address _from = ecrecover(hash, v, r, s); require(_from != address(0), "error determining from address from signature"); require(to != address(0), "cannot use address zero"); require(!frozen[to] && !frozen[_from] && !frozen[msg.sender], "address frozen"); require(value.add(serviceFee) <= balances[_from], "insufficient funds or bad signature"); require(nextSeqs[_from] == seq, "incorrect seq"); nextSeqs[_from] = nextSeqs[_from].add(1); uint256 _principle = _transfer(_from, to, value); if (serviceFee != 0) { balances[_from] = balances[_from].sub(serviceFee); balances[msg.sender] = balances[msg.sender].add(serviceFee); emit Transfer(_from, msg.sender, serviceFee); } emit BetaDelegatedTransfer(_from, to, _principle, seq, serviceFee); return true; } /** * @dev Performs an atomic batch of transfers on behalf of the from addresses, identified by their signatures. * Lack of nested array support in arguments requires all arguments to be passed as equal size arrays where * delegated transfer number i is the combination of all arguments at index i * @param r the r signatures of the delgatedTransfer msg. * @param s the s signatures of the delgatedTransfer msg. * @param v the v signatures of the delgatedTransfer msg. * @param to The addresses to transfer to. * @param value The amounts to be transferred. * @param serviceFee optional ERC20 service fees paid to the delegate of betaDelegatedTransfer by the from address. * @param seq sequencing numbers included by the from address specific to this contract to protect from replays. * @param deadline block numbers after which the pre-signed transactions have expired. * @return A boolean that indicates if the operation was successful. */ function betaDelegatedTransferBatch( bytes32[] r, bytes32[] s, uint8[] v, address[] to, uint256[] value, uint256[] serviceFee, uint256[] seq, uint256[] deadline ) public returns (bool) { require(r.length == s.length && r.length == v.length && r.length == to.length && r.length == value.length, "length mismatch"); require(r.length == serviceFee.length && r.length == seq.length && r.length == deadline.length, "length mismatch"); for (uint i = 0; i < r.length; i++) { require( _betaDelegatedTransfer(r[i], s[i], v[i], to[i], value[i], serviceFee[i], seq[i], deadline[i]), "failed transfer" ); } return true; } /** * @dev Gets whether the address is currently whitelisted for betaDelegateTransfer. * @param _addr The address to check if whitelisted. * @return A bool representing whether the given address is whitelisted. */ function isWhitelistedBetaDelegate(address _addr) public view returns (bool) { return betaDelegateWhitelist[_addr]; } /** * @dev Sets a new betaDelegate whitelister. * @param _newWhitelister The address allowed to whitelist betaDelegates. */ function setBetaDelegateWhitelister(address _newWhitelister) public { require(msg.sender == betaDelegateWhitelister || msg.sender == owner, "only Whitelister or Owner"); betaDelegateWhitelister = _newWhitelister; emit BetaDelegateWhitelisterSet(betaDelegateWhitelister, _newWhitelister); } modifier onlyBetaDelegateWhitelister() { require(msg.sender == betaDelegateWhitelister, "onlyBetaDelegateWhitelister"); _; } /** * @dev Whitelists an address to allow calling BetaDelegatedTransfer. * @param _addr The new address to whitelist. */ function whitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister { require(!betaDelegateWhitelist[_addr], "delegate already whitelisted"); betaDelegateWhitelist[_addr] = true; emit BetaDelegateWhitelisted(_addr); } /** * @dev Unwhitelists an address to disallow calling BetaDelegatedTransfer. * @param _addr The new address to whitelist. */ function unwhitelistBetaDelegate(address _addr) public onlyBetaDelegateWhitelister { require(betaDelegateWhitelist[_addr], "delegate not whitelisted"); betaDelegateWhitelist[_addr] = false; emit BetaDelegateUnwhitelisted(_addr); } // FEE CONTROLLER FUNCTIONALITY /** * @dev Sets a new fee controller address. * @param _newFeeController The address allowed to set the fee rate and the fee recipient. */ function setFeeController(address _newFeeController) public { require(msg.sender == feeController || msg.sender == owner, "only FeeController or Owner"); require(_newFeeController != address(0), "cannot set fee controller to address zero"); address _oldFeeController = feeController; feeController = _newFeeController; emit FeeControllerSet(_oldFeeController, feeController); } modifier onlyFeeController() { require(msg.sender == feeController, "only FeeController"); _; } /** * @dev Sets a new fee recipient address. * @param _newFeeRecipient The address allowed to collect transfer fees for transfers. */ function setFeeRecipient(address _newFeeRecipient) public onlyFeeController { require(_newFeeRecipient != address(0), "cannot set fee recipient to address zero"); address _oldFeeRecipient = feeRecipient; feeRecipient = _newFeeRecipient; emit FeeRecipientSet(_oldFeeRecipient, feeRecipient); } /** * @dev Sets a new fee rate. * @param _newFeeRate The new fee rate to collect as transfer fees for transfers. */ function setFeeRate(uint256 _newFeeRate) public onlyFeeController { require(_newFeeRate <= feeParts, "cannot set fee rate above 100%"); uint256 _oldFeeRate = feeRate; feeRate = _newFeeRate; emit FeeRateSet(_oldFeeRate, feeRate); } /** * @dev Gets a fee for a given value * ex: given feeRate = 200 and feeParts = 1,000,000 then getFeeFor(10000) = 2 * @param _value The amount to get the fee for. */ function getFeeFor(uint256 _value) public view returns (uint256) { if (feeRate == 0) { return 0; } return _value.mul(feeRate).div(feeParts); } }