Created
October 4, 2020 09:52
-
-
Save EdsonAlcala/b31ccea8bfbf1fd93dd9beb529ff81b2 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.7.2+commit.51b20bc0.js&optimize=false&gist=
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
contract Oracle { | |
mapping(bytes32 => bool) public supportedIdentifier; | |
// Represents the status a price request has. | |
enum RequestStatus { | |
Active, // Is being voted on in the current round. | |
Resolved // Was resolved in a previous round. | |
} | |
struct PriceRequest { | |
bytes32 identifier; | |
uint256 time; | |
RequestStatus status; | |
uint256 price; | |
} | |
mapping(bytes32 => PriceRequest) public priceRequests; | |
constructor() { | |
supportedIdentifier["ETH/USD"] = true; | |
} | |
function getPrice(bytes32 _priceIdentifier, uint256 _requestedTime) public view returns (uint256) { | |
return priceRequests[_encodePriceRequest(_priceIdentifier, _requestedTime)].price; | |
} | |
function setPrice(bytes32 _requestId, uint256 _price) public { | |
PriceRequest storage priceRequest = priceRequests[_requestId]; | |
priceRequest.status = RequestStatus.Resolved; | |
priceRequest.price = _price; | |
} | |
function requestPrice(bytes32 _identifier, uint256 _time) external { | |
require(supportedIdentifier[_identifier], "Unsupported identifier request"); | |
bytes32 priceRequestId = _encodePriceRequest(_identifier, _time); | |
priceRequests[priceRequestId] = PriceRequest({ | |
identifier: _identifier, | |
time: _time, | |
status: RequestStatus.Active, | |
price: 0 | |
}); | |
} | |
function _encodePriceRequest(bytes32 identifier, uint256 time) private pure returns (bytes32) { | |
return keccak256(abi.encode(identifier, time)); | |
} | |
} | |
// The mechanism to resolve the price could be different for the protocol. |
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
import "./SyntheticToken.sol"; | |
import "./TokenFactory.sol"; | |
import "./Timer.sol"; | |
import "./Oracle.sol"; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/token/ERC20/IERC20.sol"; | |
contract PricelessFinancialContract { | |
address public collateralAddress; | |
address public oracleAddress; | |
address public timerAddress; | |
IERC20 public collateralCurrency; | |
SyntheticToken public tokenCurrency; | |
// synthetic information | |
address public syntheticAddress; | |
string public syntheticName; | |
string public syntheticSymbol; | |
// settings | |
uint256 collateralizationRatio = 125; | |
uint256 minSponsorTokens = 100; | |
uint256 public expiryPrice; | |
uint256 public expirationTimestamp; | |
bytes32 public priceFeedIdentifier; | |
// liquidaton settings | |
uint256 public liquidationLiveness; | |
struct Position { | |
uint256 collateralAmount; | |
uint256 syntheticTokens; | |
} | |
// Mapping of sponsor addresses to their positions. Each sponsor can have only one position. | |
mapping(address => Position) public positions; | |
enum Status { PreDispute, PendingDispute, DisputeSucceeded, DisputeFailed } | |
struct Liquidation { | |
address sponsor; // Address of the liquidated position's sponsor | |
address liquidator; // Address who created this liquidation | |
Status state; // Liquidated (and expired or not), Pending a Dispute, or Dispute has resolved | |
uint256 liquidationTime; // Time when liquidation is initiated, needed to get price from Oracle | |
// Following variables determined by the position that is being liquidated: | |
uint256 tokensLiquidated; // Synthetic tokens required to be burned by liquidator to initiate dispute | |
uint256 collateralLiquidated; // Collateral locked by contract and released upon expiry or post-dispute | |
// Following variable set upon initiation of a dispute: | |
address disputer; // Person who is disputing a liquidation | |
// Following variable set upon a resolution of a dispute: | |
uint256 settlementPrice; // Final price as determined by an Oracle following a dispute | |
} | |
mapping(address => Liquidation[]) public liquidations; | |
constructor(address _collateralAddress, address _oracleAddress, address _timerAddress, address _tokenFactoryAddress, string memory _syntheticName, | |
string memory _syntheticSymbol, uint256 _expirationTimestamp, uint256 _liquidationLiveness) { | |
collateralAddress = _collateralAddress; | |
oracleAddress = _oracleAddress; | |
timerAddress = _timerAddress; | |
syntheticSymbol = _syntheticSymbol; | |
syntheticName = _syntheticName; | |
expirationTimestamp = _expirationTimestamp; | |
TokenFactory tf = TokenFactory(_tokenFactoryAddress); | |
tokenCurrency = tf.createToken(_syntheticName, _syntheticSymbol); | |
collateralCurrency = IERC20(_collateralAddress); | |
liquidationLiveness = _liquidationLiveness; | |
} | |
modifier onlyPreExpiration { | |
require(getCurrentTime() < expirationTimestamp, "Only callable pre-expiry"); | |
_; | |
} | |
function createPosition(uint256 _collateralAmount, uint256 _numTokens) public onlyPreExpiration { | |
Position storage position = positions[msg.sender]; | |
require(_checkCollateralization(_collateralAmount, _numTokens), "Insufficient collateral"); | |
require(_numTokens >= minSponsorTokens, "Below minimum sponsor position"); | |
position.collateralAmount = position.collateralAmount + _collateralAmount; // TODO add safe | |
position.syntheticTokens = position.syntheticTokens + _numTokens; // TODO add safe | |
collateralCurrency.transferFrom(msg.sender, address(this), _collateralAmount); | |
require(tokenCurrency.mint(msg.sender, _numTokens), "Minting synthetic tokens failed"); | |
} | |
function liquidatePosition(address _sponsor, uint256 _tokenToLiquidate) public returns (uint256 liquidationId){ | |
Position storage position = positions[_sponsor]; | |
require(_tokenToLiquidate == position.syntheticTokens, "Invalid number of tokens to liquidate"); | |
uint256 feeBond = position.collateralAmount; | |
// we remove the position | |
delete positions[_sponsor]; | |
liquidationId = liquidations[_sponsor].length; | |
liquidations[_sponsor].push( | |
Liquidation({ | |
sponsor: _sponsor, | |
liquidator: msg.sender, | |
state: Status.PreDispute, | |
liquidationTime: getCurrentTime(), | |
tokensLiquidated: _tokenToLiquidate, | |
collateralLiquidated: feeBond, | |
disputer: address(0), | |
settlementPrice: 0 | |
}) | |
); | |
// Destroy tokens | |
tokenCurrency.transferFrom(msg.sender, address(this), _tokenToLiquidate); | |
tokenCurrency.burn(_tokenToLiquidate); | |
// Pull final fee from liquidator. | |
collateralCurrency.transferFrom(msg.sender, address(this), feeBond); | |
} | |
function disputePosition(uint256 _liquidationId, address _sponsor) public { | |
Liquidation storage disputedLiquidation = _getLiquidationData(_sponsor, _liquidationId); | |
uint256 disputeBondAmount = disputedLiquidation.collateralLiquidated; | |
// Request a price from DVM. Liquidation is pending dispute until DVM returns a price. | |
disputedLiquidation.state = Status.PendingDispute; | |
disputedLiquidation.disputer = msg.sender; | |
// Enqueue a request with the DVM. | |
_requestOraclePrice(disputedLiquidation.liquidationTime); | |
// Transfer the dispute bond amount from the caller to this contract. | |
collateralCurrency.transferFrom(msg.sender, address(this), disputeBondAmount); | |
} | |
function _checkCollateralization(uint256 _collateralAmount, uint256 _numTokens) internal view returns (bool hasEnoughCollateral) { | |
uint256 thisCrRatio = _collateralAmount / _numTokens; // CAREFUL as this is not 100 % SAFE | |
hasEnoughCollateral = thisCrRatio > collateralizationRatio; | |
} | |
function _getPositionData(address sponsor) internal view returns (Position storage) { | |
return positions[sponsor]; | |
} | |
function _getLiquidationData(address _sponsor, uint256 _liquidationId) internal view returns (Liquidation storage liquidation) { | |
Liquidation[] storage liquidationArray = liquidations[_sponsor]; | |
return liquidationArray[_liquidationId]; | |
} | |
function getCurrentTime() public view returns(uint256 currentTime) { | |
currentTime = Timer(timerAddress).getCurrentTime(); | |
} | |
function _requestOraclePrice(uint256 _requestedTime) internal { | |
Oracle oracle = Oracle(oracleAddress); | |
oracle.requestPrice(priceFeedIdentifier, _requestedTime); | |
} | |
// function _getOraclePrice(uint256 requestedTime) internal view returns (FixedPoint.Unsigned memory) { | |
// // Create an instance of the oracle and get the price. If the price is not resolved revert. | |
// OracleInterface oracle = _getOracle(); | |
// require(oracle.hasPrice(priceIdentifier, requestedTime), "Unresolved oracle price"); | |
// int256 oraclePrice = oracle.getPrice(priceIdentifier, requestedTime); | |
// // For now we don't want to deal with negative prices in positions. | |
// if (oraclePrice < 0) { | |
// oraclePrice = 0; | |
// } | |
// return FixedPoint.Unsigned(uint256(oraclePrice)); | |
// } | |
// createPosition | |
// liquidatePosition | |
// disputePosition | |
// settlePosition; | |
// settleContract | |
// addCollateral | |
// withdrawCollateral | |
} | |
// The goal is: We create sinthetics, and the final price will tell us how much we get. | |
// To simplify the contract complexity we are not using FixedPoint library. | |
// Only full liquidations, the whole position has to be liquidated | |
// Roles, token sponsor, liquidator, disputer, uma admin, |
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/token/ERC20/ERC20.sol"; | |
contract SyntheticToken is ERC20{ | |
constructor(string memory tokenName, string memory tokenSymbol) ERC20(tokenName, tokenSymbol) { | |
} | |
function mint(address recipient, uint256 value) external returns (bool) { | |
_mint(recipient, value); | |
return true; | |
} | |
function burn(uint256 value) external { | |
_burn(msg.sender, value); | |
} | |
} |
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
contract Timer { | |
uint256 private currentTime; | |
constructor() { | |
currentTime = block.timestamp; | |
} | |
function setCurrentTime(uint256 time) external { | |
currentTime = time; | |
} | |
function getCurrentTime() public view returns (uint256) { | |
return currentTime; | |
} | |
} |
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.2.1-solc-0.7/contracts/token/ERC20/ERC20.sol"; | |
contract TokenCollateral is ERC20{ | |
constructor(string memory tokenName, string memory tokenSymbol) ERC20(tokenName, tokenSymbol) { | |
} | |
} |
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
// SPDX-License-Identifier: GPL-3.0 | |
pragma solidity ^0.7.0; | |
import "./SyntheticToken.sol"; | |
contract TokenFactory { | |
function createToken(string calldata tokenName, string calldata tokenSymbol) external returns (SyntheticToken newToken) { | |
newToken = new SyntheticToken(tokenName, tokenSymbol); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment