Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions contracts/facets/GBMFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier
//Transfer the money of the bidder to the GBM Diamond
IERC20(s.GHST).transferFrom(msg.sender, address(this), _bidAmount);

//Extend warmup time if bid is made towards warmup duration end
uint256 warmupEndTime = s.warmupEndTime[_auctionID];
if (warmupEndTime > 0) {
uint256 minWarmupWindow = warmupEndTime - s.defaultWarmupDuration;
if ((block.timestamp >= minWarmupWindow && block.timestamp < warmupEndTime) || block.timestamp == warmupEndTime) {
//extend warmup duration
s.warmupEndTime[_auctionID] = block.timestamp + s.defaultWarmupDuration;
emit Auction_WarmUpEndTimeUpdated(_auctionID, s.warmupEndTime[_auctionID]);
}
}

//Extend the duration time of the auction if we are close to the end
if (getAuctionEndTime(_auctionID) < block.timestamp + getAuctionHammerTimeDuration()) {
a.info.endTime = uint80(block.timestamp + getAuctionHammerTimeDuration());
Expand All @@ -98,10 +109,7 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier
emit Auction_BidRemoved(_auctionID, previousHighestBidder, previousHighestBid);
}

if (duePay != 0) {
s.auctions[_auctionID].auctionDebt = uint88(a.auctionDebt + duePay);
emit Auction_IncentivePaid(_auctionID, previousHighestBidder, duePay);
}
if (duePay != 0) {}

emit Auction_BidPlaced(_auctionID, msg.sender, _bidAmount);

Expand All @@ -113,8 +121,16 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier
s.auctions[_auctionID].highestBid = uint96(_bidAmount);

if (previousHighestBid + duePay != 0) {
//Refunding the previous bid as well as sending the incentives
IERC20(s.GHST).transfer(previousHighestBidder, previousHighestBid + duePay);
//only pay out incentives if not in warmup period
if (getAuctionWarmupEndTime(_auctionID) < block.timestamp) {
s.auctions[_auctionID].auctionDebt = uint88(a.auctionDebt + duePay);
//Refunding the previous bid as well as sending the incentives
IERC20(s.GHST).transfer(previousHighestBidder, previousHighestBid + duePay);
emit Auction_IncentivePaid(_auctionID, previousHighestBidder, duePay);
} else {
//only refund if still in warmup period
IERC20(s.GHST).transfer(previousHighestBidder, previousHighestBid);
}
}
}

Expand Down Expand Up @@ -209,7 +225,9 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier
function createAuction(
InitiatorInfo calldata _info,
address _tokenContract,
uint256 _auctionPresetID
uint256 _auctionPresetID,
bool _withWarmup,
uint256 _warmupPeriodInSeconds
) external returns (uint256) {
if (s.auctionPresets[_auctionPresetID].incMin < 1) revert("UndefinedPreset");
uint256 id = _info.tokenID;
Expand Down Expand Up @@ -241,6 +259,10 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier
a.info = _info;
a.presets = s.auctionPresets[_auctionPresetID];
a.biddingAllowed = true;
if (_withWarmup) {
if (_warmupPeriodInSeconds < 5 minutes) revert("MinimumWarmupPeriodNotReached");
s.warmupEndTime[_aid] = a.info.startTime + _warmupPeriodInSeconds;
}

emit Auction_Initialized(_aid, id, amount, ca, tokenKind, _auctionPresetID);
emit Auction_StartTimeUpdated(_aid, getAuctionStartTime(_aid), getAuctionEndTime(_aid));
Expand Down Expand Up @@ -578,4 +600,21 @@ contract GBMFacet is IGBM, IERC1155TokenReceiver, IERC721TokenReceiver, Modifier

return (_newBidValue * decimaledRatio) / (bidDecimals * bidDecimals);
}

function getAuctionWarmupEndTime(uint256 _auctionID) public view returns (uint256) {
uint256 warmupTime = s.warmupEndTime[_auctionID];
if (warmupTime != 0) {
return warmupTime;
} else {
return s.auctions[_auctionID].info.startTime;
}
}

function getDefaultAuctionWarmUpDuration() public view returns (uint256) {
return s.defaultWarmupDuration;
}

function setDefaultAuctionWarmupDuration(uint256 _newDefaultWarmupDuration) public onlyOwner {
s.defaultWarmupDuration = _newDefaultWarmupDuration;
}
}
1 change: 1 addition & 0 deletions contracts/interfaces/IGBM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface IGBM {

event AuctionCancelled(uint256 indexed _auctionId, uint256 _tokenId);
event RoyaltyPaid(uint256 indexed _auctionId, address indexed _tokenContractAddress, address _beneficiary, uint256 _amount);
event Auction_WarmUpEndTimeUpdated(uint256 indexed _auctionID, uint256 indexed _newWarmupEndTime);
// uint256 _auctionID,
} // uint256 _bidAmount,
// uint256 _highestBid
Expand Down
2 changes: 2 additions & 0 deletions contracts/libraries/AppStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ struct AppStorage {
uint128 hammerTimeDuration;
uint128 cancellationTime;
uint256 auctionNonce;
mapping(uint256 => uint256) warmupEndTime;
uint256 defaultWarmupDuration;
}

contract Modifiers {
Expand Down
169 changes: 169 additions & 0 deletions contracts/test/TestGBMFacet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// SPDX-License-Identifier: UNLICENSED
// © Copyright 2021. Patent pending. All rights reserved. Perpetual Altruism Ltd.
pragma solidity ^0.8.0;

import "../interfaces/IGBM.sol";
import "../interfaces/IERC20.sol";
import "../libraries/AppStorage.sol";

contract TestGBMFacet is Modifiers, IGBM {
function mockCommitBid(
uint256 _auctionID,
uint256 _bidAmount,
uint256 _highestBid,
address _tokenContract,
uint256 _tokenID,
uint256 _amount
) external {
bid(_auctionID, _tokenContract, _tokenID, _amount, _bidAmount, _highestBid);
}

function bid(
uint256 _auctionID,
address _tokenContract,
uint256 _tokenID,
uint256 _amount,
uint256 _bidAmount,
uint256 _highestBid
) internal {
Auction storage a = s.auctions[_auctionID];
if (msg.sender == a.owner) revert("OwnerBidNotAllowed");
if (a.info.startTime > block.timestamp) revert("AuctionNotStarted");
//verify existence
if (a.owner == address(0)) revert("NoAuction");
if (a.info.endTime < block.timestamp) revert("AuctionEnded");
if (a.claimed == true) revert("AuctionClaimed");
if (a.biddingAllowed == false) revert("BiddingNotAllowed");
if (_bidAmount < 1) revert("NoZeroBidAmount");
//short-circuit
if (_highestBid != a.highestBid) revert("UnmatchedHighestBid");

//Verify onchain Auction Params
if (a.tokenContract != _tokenContract) revert(" InvalidAuctionParams");
if (a.info.tokenID != _tokenID) revert(" InvalidAuctionParams");
if (a.info.tokenAmount != _amount) revert(" InvalidAuctionParams");

address tokenContract = a.tokenContract;
if (s.contractBiddingAllowed[tokenContract] == false) revert("BiddingNotAllowed");

uint256 tmp = _highestBid * mockGetAuctionBidDecimals(_auctionID);

if (tmp + mockGetAuctionStepMin(_auctionID) >= _bidAmount * mockGetAuctionBidDecimals(_auctionID)) revert("MinBidNotMet");

//Transfer the money of the bidder to the GBM Diamond
IERC20(s.GHST).transferFrom(msg.sender, address(this), _bidAmount);

//Extend warmup time if bid is made towards warmup duration end
uint256 warmupEndTime = s.warmupEndTime[_auctionID];
if (warmupEndTime > 0) {
uint256 minWarmupWindow = warmupEndTime - s.defaultWarmupDuration;
if ((block.timestamp >= minWarmupWindow && block.timestamp < warmupEndTime) || block.timestamp == warmupEndTime) {
//extend warmup duration
s.warmupEndTime[_auctionID] = block.timestamp + s.defaultWarmupDuration;
emit Auction_WarmUpEndTimeUpdated(_auctionID, s.warmupEndTime[_auctionID]);
}
}

//Extend the duration time of the auction if we are close to the end
if (mockGetAuctionEndTime(_auctionID) < block.timestamp + mockGetAuctionHammerTimeDuration()) {
a.info.endTime = uint80(block.timestamp + mockGetAuctionHammerTimeDuration());
emit Auction_EndTimeUpdated(_auctionID, a.info.endTime);
}

// Saving incentives for later sending
uint256 duePay = s.auctions[_auctionID].dueIncentives;
address previousHighestBidder = s.auctions[_auctionID].highestBidder;
uint256 previousHighestBid = s.auctions[_auctionID].highestBid;

// Emitting the event sequence
if (previousHighestBidder != address(0)) {
emit Auction_BidRemoved(_auctionID, previousHighestBidder, previousHighestBid);
}

if (duePay != 0) {}

emit Auction_BidPlaced(_auctionID, msg.sender, _bidAmount);

// Calculating incentives for the new bidder
s.auctions[_auctionID].dueIncentives = uint88(calculateIncentives(_auctionID, _bidAmount));

//Setting the new bid/bidder as the highest bid/bidder
s.auctions[_auctionID].highestBidder = msg.sender;
s.auctions[_auctionID].highestBid = uint96(_bidAmount);

if (previousHighestBid + duePay != 0) {
//only pay out incentives if not in warmup period
if (mockGetAuctionWarmupEndTime(_auctionID) < block.timestamp) {
s.auctions[_auctionID].auctionDebt = uint88(a.auctionDebt + duePay);
//Refunding the previous bid as well as sending the incentives
IERC20(s.GHST).transfer(previousHighestBidder, previousHighestBid + duePay);
emit Auction_IncentivePaid(_auctionID, previousHighestBidder, duePay);
} else {
//only refund if still in warmup period
IERC20(s.GHST).transfer(previousHighestBidder, previousHighestBid);
}
}
}

function mockGetAuctionWarmupEndTime(uint256 _auctionID) public view returns (uint256) {
uint256 warmupTime = s.warmupEndTime[_auctionID];
if (warmupTime != 0) {
return warmupTime;
} else {
return s.auctions[_auctionID].info.startTime;
}
}

function mockGetAuctionHammerTimeDuration() public view returns (uint256) {
return s.hammerTimeDuration;
}

function mockGetAuctionBidDecimals(uint256 _auctionID) public view returns (uint256) {
return s.auctions[_auctionID].presets.bidDecimals;
}

function mockGetAuctionStepMin(uint256 _auctionID) public view returns (uint64) {
return s.auctions[_auctionID].presets.stepMin;
}

function mockGetAuctionIncMin(uint256 _auctionID) public view returns (uint64) {
return s.auctions[_auctionID].presets.incMin;
}

function mockGetAuctionIncMax(uint256 _auctionID) public view returns (uint64) {
return s.auctions[_auctionID].presets.incMax;
}

function mockGetAuctionBidMultiplier(uint256 _auctionID) public view returns (uint64) {
return s.auctions[_auctionID].presets.bidMultiplier;
}

function calculateIncentives(uint256 _auctionID, uint256 _newBidValue) internal view returns (uint256) {
uint256 bidDecimals = mockGetAuctionBidDecimals(_auctionID);
uint256 bidIncMax = mockGetAuctionIncMax(_auctionID);

//Init the baseline bid we need to perform against
uint256 baseBid = (s.auctions[_auctionID].highestBid * (bidDecimals + mockGetAuctionStepMin(_auctionID))) / bidDecimals;

//If no bids are present, set a basebid value of 1 to prevent divide by 0 errors
if (baseBid == 0) {
baseBid = 1;
}

//Ratio of newBid compared to expected minBid
uint256 decimaledRatio = (bidDecimals * mockGetAuctionBidMultiplier(_auctionID) * (_newBidValue - baseBid)) /
baseBid +
mockGetAuctionIncMin(_auctionID) *
bidDecimals;

if (decimaledRatio > bidDecimals * bidIncMax) {
decimaledRatio = bidDecimals * bidIncMax;
}

return (_newBidValue * decimaledRatio) / (bidDecimals * bidDecimals);
}

function mockGetAuctionEndTime(uint256 _auctionID) public view returns (uint256) {
return s.auctions[_auctionID].info.endTime;
}
}
6 changes: 6 additions & 0 deletions scripts/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const maticGBMDiamond = "0xD5543237C656f25EEA69f1E247b8Fa59ba353306";

export const maticGBMDiamondUpgrader =
"0x585E06CA576D0565a035301819FD2cfD7104c1E8";

export const ghstAddress = "0x385Eeac5cB85A38A9a07A70c73e0a3271CfB54A7";
69 changes: 66 additions & 3 deletions scripts/helperFunctions.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Signer } from "@ethersproject/abstract-signer";
import { Contract } from "@ethersproject/contracts";
import { Contract, ContractReceipt } from "@ethersproject/contracts";
import { BigNumber } from "ethers";
import { ethers } from "hardhat";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DiamondLoupeFacet, OwnershipFacet } from "../typechain";
import { DiamondLoupeFacet, IERC20, OwnershipFacet } from "../typechain";
import { ghstAddress } from "./constants";

export const gasPrice = 100000000000;
export const gasPrice = 1664571987845;

export async function impersonate(
address: string,
Expand All @@ -15,11 +18,27 @@ export async function impersonate(
method: "hardhat_impersonateAccount",
params: [address],
});
await network.provider.send("hardhat_setBalance", [
address,
"0x100000000000000000",
]);
let signer = await ethers.getSigner(address);
contract = contract.connect(signer);
return contract;
}

export function getFunctionNames(contract: Contract) {
const signatures = Object.keys(contract.interface.functions);
const selectors = signatures.reduce((acc: string[], val: string) => {
if (val !== "init(bytes)") {
const func = contract.interface.getFunction(val);
acc.push(func.name);
}
return acc;
}, []);
return selectors;
}

export async function resetChain(hre: any) {
await hre.network.provider.request({
method: "hardhat_reset",
Expand Down Expand Up @@ -97,10 +116,54 @@ export async function getDiamondSigner(
method: "hardhat_impersonateAccount",
params: [override ? override : owner],
});

return await hre.ethers.getSigner(override ? override : owner);
} else if (hre.network.name === "matic") {
return (await hre.ethers.getSigners())[0];
} else {
throw Error("Incorrect network selected");
}
}

export function getCurrentTime(): BigNumber {
return BigNumber.from(Math.floor(Date.now() / 1000));
}
export interface InitiatorInfo {
startTime: BigNumber;
endTime: BigNumber;
tokenAmount: number;
category: number; //0 = portal 1 = open portal 2 = pending 3 = aavegotchi
tokenKind: string;
tokenID: BigNumber;
}

export function getEvent(receipt: ContractReceipt, eventName: string): any {
const events = receipt!.events!.find((event) => event.event === eventName);

return events?.args;
}

export function toEther(amount: string) {
return ethers.utils.parseEther(amount);
}

export async function getBalance(
tokenAddress: string,
recipientAddress: string
) {
const tokenContract = await ethers.getContractAt("IERC20", tokenAddress);
return await tokenContract.balanceOf(recipientAddress);
}

export async function getGHSTBalance(recipientAddress: string) {
const tokenContract = await ethers.getContractAt("IERC20", ghstAddress);
return await tokenContract.balanceOf(recipientAddress);
}

export async function warp(timeInSeconds: number) {
const newTime = await ethers.provider.send("evm_increaseTime", [
timeInSeconds + 86,
]);
await ethers.provider.send("evm_mine", []);
return newTime;
}
Loading