Skip to content

Instantly share code, notes, and snippets.

@EdsonAlcala
Created October 4, 2020 09:52
Show Gist options
  • Save EdsonAlcala/b31ccea8bfbf1fd93dd9beb529ff81b2 to your computer and use it in GitHub Desktop.
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=
// 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.
// 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,
// 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);
}
}
// 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;
}
}
// 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) {
}
}
// 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