Skip to content

Commit

Permalink
feat: abraham with bonding curve and conviction
Browse files Browse the repository at this point in the history
  • Loading branch information
bashybaranaba committed Dec 31, 2024
1 parent 818993b commit 2d0cf23
Show file tree
Hide file tree
Showing 11 changed files with 996 additions and 352 deletions.
241 changes: 145 additions & 96 deletions contracts/Abraham.sol
Original file line number Diff line number Diff line change
@@ -1,135 +1,184 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";

interface IMannaToken {
function burnFrom(address account, uint256 amount) external;
}

interface IAbrahamNFT {
function mintCreationNFT(address to, uint256 creationId) external;
}

contract Abraham is ERC1155, Ownable {
address public mannaToken;
address public abrahamNFT;
uint256 public creationCounter;
uint256 public endTimestamp;

uint256 public minimumMannaSpend = 1e18;

struct CreationData {
uint256 praises;
uint256 burns;
uint256 blessings;
uint256 totalMannaSpent;
string image; // New field for image URL
}

struct UserStats {
uint256 praiseCount;
uint256 praiseMannaSpent;
uint256 burnCount;
uint256 burnMannaSpent;
contract Abraham is Ownable {
IERC20 public immutable manna;

struct Creation {
uint256 id;
string metadataUri;
uint256 totalStaked;
uint256 praisePool;
uint256 conviction;
mapping(address => uint256) praiseBalance;
mapping(address => uint256) stakeTime;
}

mapping(uint256 => CreationData) public creations;
mapping(uint256 => mapping(address => UserStats)) public userParticipation;
uint256 public creationCount;
mapping(uint256 => Creation) public creations;
uint256[] private _allCreationIds;

event CreationReleased(uint256 indexed creationId, string image); // include image in event
event Praised(uint256 indexed creationId, address indexed user, uint256 amount);
event Burned(uint256 indexed creationId, address indexed user, uint256 amount);
event Blessed(uint256 indexed creationId, address indexed user, uint256 amount);
uint256 public initPraisePrice = 1e17; // 0.1 Manna
uint256 public initUnpraisePrice = 1e17; // 0.1 Manna

constructor(address _mannaToken, string memory uri_, address initialOwner) ERC1155(uri_) Ownable(initialOwner) {
mannaToken = _mannaToken;
endTimestamp = block.timestamp + (13 * 365 days);
struct PraiseListing {
uint256 creationId;
address seller;
uint256 amount;
uint256 pricePerPraise;
}

modifier notEnded() {
require(block.timestamp < endTimestamp, "Contract expired");
_;
}
PraiseListing[] public praiseListings;

function setMinimumMannaSpend(uint256 newMin) external onlyOwner {
minimumMannaSpend = newMin;
event CreationAdded(uint256 indexed creationId, string metadataUri);
event Praised(uint256 indexed creationId, address indexed user, uint256 pricePaid, uint256 unitsPraised);
event Unpraised(uint256 indexed creationId, address indexed user, uint256 unitsUnpraised, uint256 mannaRefunded);
event ConvictionUpdated(uint256 indexed creationId, uint256 newConviction);
event PraiseListed(uint256 listingId, uint256 creationId, address indexed seller, uint256 amount, uint256 pricePerPraise);
event PraiseSold(uint256 listingId, uint256 creationId, address indexed buyer, uint256 amount, uint256 totalCost);

constructor(address _manna, address initialOwner) Ownable(initialOwner) {
require(_manna != address(0), "Invalid token address");
manna = IERC20(_manna);
}

function setAbrahamNFT(address _abrahamNFT) external onlyOwner {
abrahamNFT = _abrahamNFT;
function newCreation(string calldata metadataUri) external onlyOwner {
creationCount++;
Creation storage c = creations[creationCount];
c.id = creationCount;
c.metadataUri = metadataUri;
_allCreationIds.push(creationCount);
emit CreationAdded(creationCount, metadataUri);
}

function releaseCreation(string memory image) external onlyOwner notEnded {
creationCounter += 1;
function praise(uint256 creationId) external {
Creation storage c = creations[creationId];
require(c.id > 0, "Creation does not exist");

creations[creationCounter] = CreationData({
praises: 0,
burns: 0,
blessings: 0,
totalMannaSpent: 0,
image: image
});
uint256 currentStaked = c.totalStaked;
uint256 priceForOne = initPraisePrice + (currentStaked * initPraisePrice);

emit CreationReleased(creationCounter, image);
bool transferred = manna.transferFrom(msg.sender, address(this), priceForOne);
require(transferred, "Manna transfer failed");

require(abrahamNFT != address(0), "AbrahamNFT not set");
IAbrahamNFT(abrahamNFT).mintCreationNFT(owner(), creationCounter);
}
c.praisePool += priceForOne;

function _spendManna(uint256 amount) internal {
require(amount >= minimumMannaSpend, "Spend more Manna");
(bool success, ) = mannaToken.call(
abi.encodeWithSignature("burnFrom(address,uint256)", msg.sender, amount)
);
require(success, "Manna burn failed");
_updateConvictionOnPraise(c, msg.sender);

c.totalStaked = currentStaked + 1;
c.praiseBalance[msg.sender]++;
emit Praised(creationId, msg.sender, priceForOne, 1);
}

function praise(uint256 creationId, uint256 amount) external notEnded {
require(creationId > 0 && creationId <= creationCounter, "Invalid creation");
_spendManna(amount);
function unpraise(uint256 creationId) external {
Creation storage c = creations[creationId];
require(c.id > 0, "Creation does not exist");
require(c.praiseBalance[msg.sender] > 0, "No praise to unpraise");

uint256 refundForOne = initUnpraisePrice;

require(c.praisePool >= refundForOne, "Not enough in praise pool");

_updateConvictionOnUnpraise(c, msg.sender);

creations[creationId].praises += 1;
creations[creationId].totalMannaSpent += amount;
c.praiseBalance[msg.sender]--;
c.totalStaked--;
c.praisePool -= refundForOne;

userParticipation[creationId][msg.sender].praiseCount += 1;
userParticipation[creationId][msg.sender].praiseMannaSpent += amount;
bool sent = manna.transfer(msg.sender, refundForOne);
require(sent, "Refund transfer failed");

_mint(msg.sender, creationId, amount, "");
emit Praised(creationId, msg.sender, amount);
emit Unpraised(creationId, msg.sender, 1, refundForOne);
}

function burnCreation(uint256 creationId, uint256 amount) external notEnded {
require(creationId > 0 && creationId <= creationCounter, "Invalid creation");
_spendManna(amount);
function listPraiseForSale(
uint256 creationId,
uint256 amount,
uint256 pricePerPraise
) external {
Creation storage c = creations[creationId];
require(c.id > 0, "Creation does not exist");
require(c.praiseBalance[msg.sender] >= amount, "Insufficient praises to sell");

praiseListings.push(PraiseListing({
creationId: creationId,
seller: msg.sender,
amount: amount,
pricePerPraise: pricePerPraise
}));

uint256 listingId = praiseListings.length - 1;
emit PraiseListed(listingId, creationId, msg.sender, amount, pricePerPraise);
}

function buyPraise(uint256 listingId, uint256 amount) external {
PraiseListing storage listing = praiseListings[listingId];
require(listing.amount >= amount, "Not enough praises available");

creations[creationId].burns += 1;
creations[creationId].totalMannaSpent += amount;
uint256 totalCost = amount * listing.pricePerPraise;
bool transferred = manna.transferFrom(msg.sender, listing.seller, totalCost);
require(transferred, "Payment failed");

userParticipation[creationId][msg.sender].burnCount += 1;
userParticipation[creationId][msg.sender].burnMannaSpent += amount;
Creation storage c = creations[listing.creationId];
c.praiseBalance[msg.sender] += amount;
c.praiseBalance[listing.seller] -= amount;

_mint(msg.sender, creationId, amount, "");
emit Burned(creationId, msg.sender, amount);
listing.amount -= amount;

emit PraiseSold(listingId, listing.creationId, msg.sender, amount, totalCost);
}

function bless(uint256 creationId, uint256 amount) external notEnded {
require(creationId > 0 && creationId <= creationCounter, "Invalid creation");
_spendManna(amount);
function _updateConvictionOnPraise(Creation storage c, address user) internal {
uint256 currentBalance = c.praiseBalance[user];
if (currentBalance > 0) {
uint256 timeHeld = block.timestamp - c.stakeTime[user];
uint256 addedConviction = currentBalance * timeHeld;
c.conviction += addedConviction;
emit ConvictionUpdated(c.id, c.conviction);
}
c.stakeTime[user] = block.timestamp;
}

creations[creationId].blessings += 1;
creations[creationId].totalMannaSpent += amount;
function _updateConvictionOnUnpraise(Creation storage c, address user) internal {
uint256 currentBalance = c.praiseBalance[user];
uint256 timeHeld = block.timestamp - c.stakeTime[user];
uint256 addedConviction = currentBalance * timeHeld;
c.conviction += addedConviction;
emit ConvictionUpdated(c.id, c.conviction);
c.stakeTime[user] = block.timestamp;
}

function getCreation(uint256 creationId)
external
view
returns (
uint256 id,
string memory uri,
uint256 totalStaked,
uint256 praisePool,
uint256 conviction
)
{
Creation storage c = creations[creationId];
id = c.id;
uri = c.metadataUri;
totalStaked = c.totalStaked;
praisePool = c.praisePool;
conviction = c.conviction;
}

_mint(msg.sender, creationId, amount, "");
emit Blessed(creationId, msg.sender, amount);
function getUserPraise(uint256 creationId, address user) external view returns (uint256) {
return creations[creationId].praiseBalance[user];
}

function setMannaToken(address _mannaToken) external onlyOwner notEnded {
mannaToken = _mannaToken;
function allCreationIds() external view returns (uint256[] memory) {
return _allCreationIds;
}

function setURI(string memory newuri) external onlyOwner {
_setURI(newuri);
function getPraiseListings() external view returns (PraiseListing[] memory) {
return praiseListings;
}
}
}
54 changes: 54 additions & 0 deletions contracts/Manna.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Manna is ERC20, Ownable {
uint256 public constant INITIAL_SUPPLY = 1000000 * (10 ** 18);
uint256 public constant MANNA_PRICE = 0.0001 ether;

event BoughtManna(address indexed buyer, uint256 amount);
event SoldManna(address indexed seller, uint256 mannaAmount, uint256 ethAmount);

constructor(address initialOwner) ERC20("Manna", "MANNA") Ownable(initialOwner) {
uint256 initialOwnerSupply = INITIAL_SUPPLY / 2;
_mint(initialOwner, initialOwnerSupply);
}

function buyManna() external payable {
require(msg.value >= MANNA_PRICE, "Insufficient Ether");
uint256 mannaAmount = (msg.value * (10 ** 18)) / MANNA_PRICE;
require(totalSupply() + mannaAmount <= INITIAL_SUPPLY, "Manna supply cap reached");

_mint(msg.sender, mannaAmount);
emit BoughtManna(msg.sender, mannaAmount);
}

function sellManna(uint256 mannaAmount) external {
require(balanceOf(msg.sender) >= mannaAmount, "Not enough Manna");
uint256 ethAmount = (mannaAmount * MANNA_PRICE) / (10 ** 18);
require(address(this).balance >= ethAmount, "Contract lacks Ether");

_burn(msg.sender, mannaAmount);

(bool sent, ) = msg.sender.call{value: ethAmount}("");
require(sent, "Failed to send Ether");

emit SoldManna(msg.sender, mannaAmount, ethAmount);
}

function getContractBalances() external view returns (uint256 mannaBalance, uint256 ethBalance) {
mannaBalance = balanceOf(address(this));
ethBalance = address(this).balance;
}

receive() external payable {
this.buyManna{value: msg.value}();
}

function burnFrom(address account, uint256 amount) external {
require(balanceOf(account) >= amount, "Not enough Manna to create art");
_burn(account, amount);
}
}
Loading

0 comments on commit 2d0cf23

Please sign in to comment.