Skip to content

Instantly share code, notes, and snippets.

@ngmachado
Last active October 6, 2022 13:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ngmachado/556f3b19738635958f31e922a4374e89 to your computer and use it in GitHub Desktop.
Save ngmachado/556f3b19738635958f31e922a4374e89 to your computer and use it in GitHub Desktop.
CFA Bassd NFT (offchain JSON)
pragma solidity ^0.8.0;
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { Counters } from "@openzeppelin/contracts/utils/Counters.sol";
import {IConstantFlowAgreementV1} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol";
import { ISuperfluid, ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
/*
Super721SVGOffchain - NFT that points to JSON Offchain
*/
contract Super721SVGOffChain {
using Strings for uint256;
using Counters for Counters.Counter;
struct StreamData {
address token;
address sender;
address receiver;
}
error NOT_ALLOWED();
error ALREADY_MINTED();
error NOT_MINTED();
error ZERO_ADDRESS();
error EMPTY_DATA();
error NOT_STREAM_USER();
event Transfer(address indexed from, address indexed to, uint256 indexed id);
string public name;
string public symbol;
IConstantFlowAgreementV1 public cfaV1;
Counters.Counter private _tokenIds;
mapping(uint256 => address) internal _ownerOf;
mapping(bytes32 => uint256) internal _revertStreamToId; // id = keccak(token, sender, receiver)
function tokenURI(uint256 id) public view returns (string memory) {
return string(abi.encodePacked('https://nft.x.superfluid.dev/cfa/', id.toString()));
}
constructor(address cfa, string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
cfaV1 = IConstantFlowAgreementV1(cfa);
}
function ownerOf(uint256 id) public view virtual returns (address owner) {
owner = _ownerOf[id];
if(owner == address(0)) revert NOT_MINTED();
}
function balanceOf(address owner) public view virtual returns (uint256) {
return 1;
}
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
//Mock until SF integration
function onCreate(StreamData memory newFlowData, address token) public returns(bool) {
_tokenIds.increment();
newFlowData.token = token;
_mint(_tokenIds.current(), newFlowData);
return true;
}
function onUpdate(StreamData memory updatedFlowData, address token, int96 oldFlowRate) public returns(bool) {
return true;
}
function onDelete(StreamData memory updatedFlowData, address token, int96 oldFlowRate) public returns(bool) {
_burn(token, updatedFlowData.sender, updatedFlowData.receiver);
return true;
}
function _mint(uint256 id, StreamData memory streamData) internal {
if(streamData.receiver == address(0)) revert ZERO_ADDRESS();
bytes32 reverseKey = keccak256(abi.encodePacked(
streamData.token,
streamData.sender,
streamData.receiver
));
if(_revertStreamToId[reverseKey] != 0 || _ownerOf[id] != address(0)) revert ALREADY_MINTED();
_ownerOf[id] = streamData.receiver;
_revertStreamToId[reverseKey] = id;
emit Transfer(address(0), streamData.receiver, id);
}
function _burn(address token, address sender, address receiver) internal {
bytes32 reverseKey = keccak256(abi.encodePacked(token, sender, receiver));
uint256 id = _revertStreamToId[reverseKey];
if(id == 0) revert NOT_MINTED();
delete _ownerOf[id];
delete _revertStreamToId[reverseKey];
emit Transfer(receiver, address(0), id);
}
function mint(address token, address sender, address receiver) public {
int96 flowRate = getFlowRate(token, sender, receiver);
if(flowRate > 0) {
_tokenIds.increment();
_mint(_tokenIds.current(), StreamData(token, sender, receiver));
}
}
function burn(address token, address sender, address receiver) public {
if(msg.sender != receiver) revert NOT_STREAM_USER();
int96 flowRate = getFlowRate(token, sender, receiver);
if(flowRate == 0) {
_burn(token, sender, receiver);
}
}
function approve(address spender, uint256 id) public {
revert NOT_ALLOWED();
}
function setApprovalForAll(address operator, bool approved) public {
revert NOT_ALLOWED();
}
function transferFrom(address from,address to,uint256 id) public {
revert NOT_ALLOWED();
}
function safeTransferFrom(address from,address to,uint256 id) public {
revert NOT_ALLOWED();
}
function safeTransferFrom(address from, address to, uint256 id,bytes calldata data) public {
revert NOT_ALLOWED();
}
function getFlowRate(address token, address sender, address receiver) public view returns(int96 flowRate) {
(,flowRate,,) = cfaV1.getFlow(ISuperToken(token), sender, receiver);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment