Created
September 26, 2023 17:21
-
-
Save OxMarco/910918a52c45dd01f9ad7ded2ef07c27 to your computer and use it in GitHub Desktop.
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: MIT | |
pragma solidity ^0.8.19; | |
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
import { Counters } from "@openzeppelin/contracts/utils/Counters.sol"; | |
import { IERC20, SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
contract CDS is ERC721 { | |
using SafeERC20 for IERC20; | |
using Counters for Counters.Counter; | |
struct SwapData { | |
IERC20 token; | |
address source; | |
uint256 amount; | |
uint256 premium; | |
uint256 deadline; | |
uint256 maturity; | |
bool active; | |
} | |
enum Status { CANCELLED, TRIGGERED, UNTRIGGERED } | |
event Created(uint256 indexed tokenID, SwapData data); | |
event Subscribed(uint256 indexed tokenID, address indexed subscriber); | |
event Closed(uint256 indexed tokenID, address indexed owner, SwapData data, Status indexed status); | |
error Revert(string reason); | |
Counters.Counter public id; | |
mapping(uint256 tokenID => SwapData data) public data; | |
mapping(uint256 tokenID => string url) public metadata; | |
constructor() ERC721("Credit Default Swap", "CDS") {} | |
modifier exists(uint256 tokenID) { | |
if(!_exists(tokenID)) revert Revert({ reason: "Token does not exist" }); | |
_; | |
} | |
function create(address _token, uint256 _amount, uint256 _premium, uint256 _deadline, uint256 _maturity, string memory _url) external { | |
if(_maturity < block.timestamp) revert Revert({ reason: "Invalid maturity date" }); | |
if(_maturity > _deadline) revert Revert({ reason: "Invalid deadline date" }); | |
IERC20 token = IERC20(_token); | |
token.safeTransferFrom(msg.sender, address(this), _premium); | |
id.increment(); | |
data[id.current()] = SwapData({ | |
token: token, | |
source: msg.sender, | |
amount: _amount, | |
premium: _premium, | |
deadline: _deadline, | |
maturity: _maturity, | |
active: false | |
}); | |
metadata[id.current()] = _url; | |
_safeMint(msg.sender, id.current()); | |
emit Created(id.current(), data[id.current()]); | |
} | |
function subscribe(uint256 tokenID) external exists(tokenID) { | |
SwapData storage d = data[tokenID]; | |
if(d.active) revert Revert({ reason: "Subscribed already" }); | |
if(d.deadline < block.timestamp) revert Revert({ reason: "Expired" }); | |
if(d.maturity > block.timestamp) revert Revert({ reason: "Matured already" }); | |
d.active = true; | |
d.token.safeTransferFrom(msg.sender, address(this), d.amount); | |
emit Subscribed(tokenID, msg.sender); | |
} | |
function close(uint256 tokenID) external exists(tokenID) { | |
SwapData memory d = data[tokenID]; | |
if(d.maturity < block.timestamp) revert Revert({ reason: "Not matured yet" }); | |
Status status = Status.CANCELLED; | |
address owner = ownerOf(tokenID); | |
if(!d.active) { | |
d.token.safeTransfer(d.source, d.amount); | |
} else { | |
status = _handlePayoff(d, owner); | |
} | |
_burn(tokenID); | |
delete data[tokenID]; | |
delete metadata[tokenID]; | |
emit Closed(tokenID, owner, d, status); | |
} | |
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { | |
string memory customMetadata = metadata[tokenId]; | |
string memory baseURI = _baseURI(); | |
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, customMetadata)) : customMetadata; | |
} | |
function _handlePayoff(SwapData memory d, address owner) internal returns (Status) { | |
uint256 notional = d.amount + d.premium; | |
// TODO use oracle | |
bool outcome = true; | |
Status status; | |
if(outcome) { | |
d.token.safeTransfer(d.source, notional); | |
status = Status.UNTRIGGERED; | |
} else { | |
d.token.safeTransfer(owner, notional); | |
status = Status.TRIGGERED; | |
} | |
return status; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment