Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"printWidth": 120
}
219 changes: 124 additions & 95 deletions contracts/tokens/WrappedPinakion.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,51 @@ pragma solidity ^0.4.24;

import "openzeppelin-eth/contracts/zos-lib/Initializable.sol";
import "openzeppelin-eth/contracts/math/SafeMath.sol";
import { TokenController } from "minimetoken/contracts/TokenController.sol";
import { ITokenBridge } from "../interfaces/ITokenBridge.sol";
import { IERC677 } from "../interfaces/IERC677.sol";
import {TokenController} from "minimetoken/contracts/TokenController.sol";
import {ITokenBridge} from "../interfaces/ITokenBridge.sol";
import {IERC677} from "../interfaces/IERC677.sol";

contract WrappedPinakion is Initializable {

using SafeMath for uint256;

/* Events */

/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
*
* Note that `value` may be zero.
*/
* @notice Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
* @dev Notice that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
* @notice Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);

/* Storage */

mapping (address => uint256) private balances;
mapping (address => mapping (address => uint256)) public allowance;
mapping(address => uint256) private balances;
mapping(address => mapping(address => uint256)) public allowance;

/// @dev Total supply of the token. Equals the total xPinakion deposit into the contract.
/// @notice Total supply of the token. Equals the total xPinakion deposit into the contract.
uint256 public totalSupply;

/// @dev Name of the token.
/// @notice Name of the token.
string public name;

/// @dev Symbol of the token.
/// @notice Symbol of the token.
string public symbol;

/// @dev Number of decimals of the token.
/// @notice Number of decimals of the token.
uint8 public decimals;

/// @dev The token's controller.
/// @notice The token's controller.
address public controller;

/// @dev Pinakion on xDai to be wrapped. This token is upgradeable.
/// @notice Pinakion on xDai to be wrapped. This token is upgradeable.
IERC677 public xPinakion;

/// @dev xDai Token Bridge. The Token Bridge is upgradeable.
/// @notice xDai Token Bridge. The Token Bridge is upgradeable.
ITokenBridge public tokenBridge;

/* Modifiers */
Expand All @@ -69,16 +67,17 @@ contract WrappedPinakion is Initializable {

/* Initializer */

/** @dev Constructor.
* @param _name for the wrapped Pinakion on the home chain.
* @param _symbol for wrapped Pinakion ticker on the home chain.
* @param _xPinakion the home pinakion contract which is already bridged to the foreign pinakion contract.
* @param _tokenBridge the TokenBridge contract.
/**
* @dev Constructor.
* @param _name for the wrapped Pinakion on the home chain.
* @param _symbol for wrapped Pinakion ticker on the home chain.
* @param _xPinakion the home pinakion contract which is already bridged to the foreign pinakion contract.
* @param _tokenBridge the TokenBridge contract.
*/
function initialize(
string memory _name,
string memory _symbol,
IERC677 _xPinakion,
string memory _name,
string memory _symbol,
IERC677 _xPinakion,
ITokenBridge _tokenBridge
) public initializer {
name = _name;
Expand All @@ -92,33 +91,40 @@ contract WrappedPinakion is Initializable {

/* External */

/** @dev Changes `controller` to `_controller`.
* @param _controller The new controller of the contract
/**
* @notice Changes `controller` to `_controller`.
* @param _controller The new controller of the contract
*/
function changeController(address _controller) external onlyController {
controller = _controller;
}


/** @dev Converts bridged pinakions into pinakions which can be staked in KlerosLiquid.
* @param _amount The amount of wrapped pinakions to mint.
/**
* @notice Converts bridged pinakions into pinakions which can be staked in KlerosLiquid.
* @param _amount The amount of wrapped pinakions to mint.
*/
function deposit(uint _amount) external {
function deposit(uint256 _amount) external {
_mint(msg.sender, _amount);
require(xPinakion.transferFrom(msg.sender, address(this), _amount), "Sender does not have enough approved funds.");
require(
xPinakion.transferFrom(msg.sender, address(this), _amount),
"Sender does not have enough approved funds."
);
}


/** @dev IERC20 Receiver functionality.
* Converts bridged PNK (xPinakion) into wrapped PNK which can be staked in KlerosLiquid.
* If the tokenBridge is calling this function, then this contract has already received
* the xPinakion tokens.
* @param _token The token address the _amount belongs to.
* @param _amount The amount of wrapped pinakions to mint.
* @param _data Calldata containing the address of the recipient.
* Notice that the address has to be padded to 32 bytes.
/**
* @notice IERC20 Receiver functionality.
* @dev Converts bridged PNK (xPinakion) into wrapped PNK which can be staked in KlerosLiquid.
* If the tokenBridge is calling this function, then this contract has already received
* the xPinakion tokens.
* @param _token The token address the _amount belongs to.
* @param _amount The amount of wrapped pinakions to mint.
* @param _data Calldata containing the address of the recipient. Notice that the address has to be padded to 32 bytes.
*/
function onTokenBridged(address _token, uint _amount, bytes _data) external {
function onTokenBridged(
address _token,
uint256 _amount,
bytes _data
) external {
require(msg.sender == address(tokenBridge), "Sender not authorized.");
require(_token == address(xPinakion), "Token bridged is not xPinakion.");

Expand All @@ -129,30 +135,34 @@ contract WrappedPinakion is Initializable {
_mint(recipient, _amount);
}

/** @dev Withdraws bridged pinakions.
* @param _amount The amount of bridged pinakions to withdraw.
/**
* @notice Withdraws bridged pinakions.
* @param _amount The amount of bridged pinakions to withdraw.
*/
function withdraw(uint _amount) external {
function withdraw(uint256 _amount) external {
_burn(_amount);
require(xPinakion.transfer(msg.sender, _amount), "The `transfer` function must not fail.");
}

/** @dev This function is not strictly needed, but it provides a good UX to users who want to get their Mainnet's PNK back.
* What normally takes 3 transactions, here is done in one go.
* Notice that the PNK have to be claimed on mainnet's TokenBride by the receiver.
* @param _amount The amount of bridged pinakions to withdraw.
* @param _receiver The address which will receive the PNK back in the foreign chain.
*/
function withdrawAndConvertToPNK(uint _amount, address _receiver) external {
/**
* @notice Withdraws the WrappedPinakion and transfers it through the Token Bridge.
* @dev This function is not strictly needed, but it provides a good UX to users who want to get their Mainnet's PNK back.
* What normally takes 3 transactions, here is done in one go.
* Notice that the PNK have to be claimed on mainnet's TokenBride by the receiver.
* @param _amount The amount of bridged pinakions to withdraw.
* @param _receiver The address which will receive the PNK back in the foreign chain.
*/
function withdrawAndConvertToPNK(uint256 _amount, address _receiver) external {
_burn(_amount);
// Using approve is safe here, because this contract approves the bridge to spend the tokens and triggers the relay immediately.
xPinakion.approve(address(tokenBridge), _amount);
tokenBridge.relayTokens(xPinakion, _receiver, _amount);
}

/** @dev Moves `_amount` tokens from the caller's account to `_recipient`.
* @param _recipient The entity receiving the funds.
* @param _amount The amount to tranfer in base units.
/**
* @notice Moves `_amount` tokens from the caller's account to `_recipient`.
* @param _recipient The entity receiving the funds.
* @param _amount The amount to tranfer in base units.
*/
function transfer(address _recipient, uint256 _amount) public returns (bool) {
if (isContract(controller)) {
Expand All @@ -164,13 +174,18 @@ contract WrappedPinakion is Initializable {
return true;
}

/** @dev Moves `_amount` tokens from `_sender` to `_recipient` using the
* allowance mechanism. `_amount` is then deducted from the caller's allowance.
* @param _sender The entity to take the funds from.
* @param _recipient The entity receiving the funds.
* @param _amount The amount to tranfer in base units.
/**
* @notice Moves `_amount` tokens from `_sender` to `_recipient` using the
* allowance mechanism. `_amount` is then deducted from the caller's allowance.
* @param _sender The entity to take the funds from.
* @param _recipient The entity receiving the funds.
* @param _amount The amount to tranfer in base units.
*/
function transferFrom(address _sender, address _recipient, uint256 _amount) public returns (bool) {
function transferFrom(
address _sender,
address _recipient,
uint256 _amount
) public returns (bool) {
if (isContract(controller)) {
require(TokenController(controller).onTransfer(_sender, _recipient, _amount));
}
Expand All @@ -183,53 +198,65 @@ contract WrappedPinakion is Initializable {
if (msg.sender != controller) {
allowance[_sender][msg.sender] = allowance[_sender][msg.sender].sub(_amount); // ERC20: transfer amount exceeds allowance.
}

balances[_sender] = balances[_sender].sub(_amount); // ERC20: transfer amount exceeds balance
balances[_recipient] = balances[_recipient].add(_amount);
emit Transfer(_sender, _recipient, _amount);
return true;
}

/** @dev Approves `_spender` to spend `_amount`.
* @param _spender The entity allowed to spend funds.
* @param _amount The amount of base units the entity will be allowed to spend.
/**
* @notice Approves `_spender` to spend `_amount`.
* @param _spender The entity allowed to spend funds.
* @param _amount The amount of base units the entity will be allowed to spend.
*/
function approve(address _spender, uint256 _amount) public returns (bool) {
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(msg.sender, _spender, _amount), "Token controller does not approve.");
require(
TokenController(controller).onApprove(msg.sender, _spender, _amount),
"Token controller does not approve."
);
}

allowance[msg.sender][_spender] = _amount;
emit Approval(msg.sender, _spender, _amount);
return true;
}

/** @dev Increases the `_spender` allowance by `_addedValue`.
* @param _spender The entity allowed to spend funds.
* @param _addedValue The amount of extra base units the entity will be allowed to spend.
/**
* @notice Increases the `_spender` allowance by `_addedValue`.
* @param _spender The entity allowed to spend funds.
* @param _addedValue The amount of extra base units the entity will be allowed to spend.
*/
function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) {
uint256 newAllowance = allowance[msg.sender][_spender].add(_addedValue);
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(msg.sender, _spender, newAllowance), "Token controller does not approve.");
require(
TokenController(controller).onApprove(msg.sender, _spender, newAllowance),
"Token controller does not approve."
);
}

allowance[msg.sender][_spender] = newAllowance;
emit Approval(msg.sender, _spender, newAllowance);
return true;
}

/** @dev Decreases the `_spender` allowance by `_subtractedValue`.
* @param _spender The entity whose spending allocation will be reduced.
* @param _subtractedValue The reduction of spending allocation in base units.
/**
* @notice Decreases the `_spender` allowance by `_subtractedValue`.
* @param _spender The entity whose spending allocation will be reduced.
* @param _subtractedValue The reduction of spending allocation in base units.
*/
function decreaseAllowance(address _spender, uint256 _subtractedValue) public returns (bool) {
uint256 newAllowance = allowance[msg.sender][_spender].sub(_subtractedValue); // ERC20: decreased allowance below zero
// Alerts the token controller of the approve function call
if (isContract(controller)) {
require(TokenController(controller).onApprove(msg.sender, _spender, newAllowance), "Token controller does not approve.");
require(
TokenController(controller).onApprove(msg.sender, _spender, newAllowance),
"Token controller does not approve."
);
}

allowance[msg.sender][_spender] = newAllowance;
Expand All @@ -240,20 +267,21 @@ contract WrappedPinakion is Initializable {
/* Internal */

/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param _recipient The address which will receive the minted tokens.
* @param _amount The amount that will be created.
*/
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param _recipient The address which will receive the minted tokens.
* @param _amount The amount that will be created.
*/
function _mint(address _recipient, uint256 _amount) internal {
totalSupply = totalSupply.add(_amount);
balances[_recipient] = balances[_recipient].add(_amount);
emit Transfer(address(0x0), _recipient, _amount);
}

/** @dev Destroys `_amount` tokens from the caller. Cannot burn locked tokens.
* @param _amount The quantity of tokens to burn in base units.
/**
* @dev Destroys `_amount` tokens from the caller. Cannot burn locked tokens.
* @param _amount The quantity of tokens to burn in base units.
*/
function _burn(uint256 _amount) internal {
if (isContract(controller)) {
Expand All @@ -264,12 +292,13 @@ contract WrappedPinakion is Initializable {
emit Transfer(msg.sender, address(0x0), _amount);
}

/** @dev Internal function to determine if an address is a contract.
* @param _addr The address being queried.
* @return True if `_addr` is a contract.
/**
* @dev Internal function to determine if an address is a contract.
* @param _addr The address being queried.
* @return True if `_addr` is a contract.
*/
function isContract(address _addr) internal view returns(bool) {
uint size;
function isContract(address _addr) internal view returns (bool) {
uint256 size;
if (_addr == 0) return false;
assembly {
size := extcodesize(_addr)
Expand All @@ -280,11 +309,11 @@ contract WrappedPinakion is Initializable {
/* Getters */

/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the balance of.
* @return uint256 value representing the amount owned by the passed address.
*/
* @dev Gets the balance of the specified address.
* @param _owner The address to query the balance of.
* @return uint256 value representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256) {
return balances[_owner];
}
}
}