Skip to main content

Improv auction (Ethereum)

The Improv auction Ethereum contract is written in Solidity, and it facilitates casting auctions and arbitration on the Ethereum network. For NFTs minted on the Terra chain, visit the Improv auction (Terra).

Messages

Initialize

  • function initialize() initializes the auction contract.
ImprovAuction.sol
Copy

_28
function initialize(
_28
bytes32 improvHub,
_28
address improvRelayer,
_28
address bidToken,
_28
address arbitrationNFT,
_28
address wormholeCoreBridge,
_28
address wormholeTokenBridge,
_28
uint16 currentChainId,
_28
uint8 wrappedTokenDecimals
_28
) public initializer {
_28
__Ownable_init();
_28
__UUPSUpgradeable_init();
_28
IMPROV_HUB = improvHub;
_28
IMPROV_RELAYER = improvRelayer;
_28
BID_TOKEN = bidToken;
_28
ARBITRATION_NFT = arbitrationNFT;
_28
WORMHOLE_CORE_BRIDGE = wormholeCoreBridge;
_28
WORMHOLE_TOKEN_BRIDGE = wormholeTokenBridge;
_28
CURRENT_CHAIN_ID = currentChainId;
_28
WRAPPED_BID_TOKEN_DECIMALS = wrappedTokenDecimals;
_28
(, bytes memory queried_decimals) = address(BID_TOKEN).staticcall(
_28
abi.encodeWithSignature('decimals()')
_28
);
_28
BID_TOKEN_DECIMALS = abi.decode(queried_decimals, (uint8));
_28
_28
// the type of tokens on terra is uint128, so its decimals must be smaller.
_28
require(WRAPPED_BID_TOKEN_DECIMALS <= BID_TOKEN_DECIMALS);
_28
}

ImprovAuction.sol
Copy

_28
function initialize(
_28
bytes32 improvHub,
_28
address improvRelayer,
_28
address bidToken,
_28
address arbitrationNFT,
_28
address wormholeCoreBridge,
_28
address wormholeTokenBridge,
_28
uint16 currentChainId,
_28
uint8 wrappedTokenDecimals
_28
) public initializer {
_28
__Ownable_init();
_28
__UUPSUpgradeable_init();
_28
IMPROV_HUB = improvHub;
_28
IMPROV_RELAYER = improvRelayer;
_28
BID_TOKEN = bidToken;
_28
ARBITRATION_NFT = arbitrationNFT;
_28
WORMHOLE_CORE_BRIDGE = wormholeCoreBridge;
_28
WORMHOLE_TOKEN_BRIDGE = wormholeTokenBridge;
_28
CURRENT_CHAIN_ID = currentChainId;
_28
WRAPPED_BID_TOKEN_DECIMALS = wrappedTokenDecimals;
_28
(, bytes memory queried_decimals) = address(BID_TOKEN).staticcall(
_28
abi.encodeWithSignature('decimals()')
_28
);
_28
BID_TOKEN_DECIMALS = abi.decode(queried_decimals, (uint8));
_28
_28
// the type of tokens on terra is uint128, so its decimals must be smaller.
_28
require(WRAPPED_BID_TOKEN_DECIMALS <= BID_TOKEN_DECIMALS);
_28
}

  • wrappedTokenDecimals: the decimals of the Wormhole-wrapped bid token on Terra.
ImprovAuction.sol
Copy

_28
function initialize(
_28
bytes32 improvHub,
_28
address improvRelayer,
_28
address bidToken,
_28
address arbitrationNFT,
_28
address wormholeCoreBridge,
_28
address wormholeTokenBridge,
_28
uint16 currentChainId,
_28
uint8 wrappedTokenDecimals
_28
) public initializer {
_28
__Ownable_init();
_28
__UUPSUpgradeable_init();
_28
IMPROV_HUB = improvHub;
_28
IMPROV_RELAYER = improvRelayer;
_28
BID_TOKEN = bidToken;
_28
ARBITRATION_NFT = arbitrationNFT;
_28
WORMHOLE_CORE_BRIDGE = wormholeCoreBridge;
_28
WORMHOLE_TOKEN_BRIDGE = wormholeTokenBridge;
_28
CURRENT_CHAIN_ID = currentChainId;
_28
WRAPPED_BID_TOKEN_DECIMALS = wrappedTokenDecimals;
_28
(, bytes memory queried_decimals) = address(BID_TOKEN).staticcall(
_28
abi.encodeWithSignature('decimals()')
_28
);
_28
BID_TOKEN_DECIMALS = abi.decode(queried_decimals, (uint8));
_28
_28
// the type of tokens on terra is uint128, so its decimals must be smaller.
_28
require(WRAPPED_BID_TOKEN_DECIMALS <= BID_TOKEN_DECIMALS);
_28
}

  • Terra tokens are uint128, so BID_TOKEN_DECIMALS decimals must be smaller than require(WRAPPED_BID_TOKEN_DECIMALS.
ImprovAuction.sol
Copy

_28
function initialize(
_28
bytes32 improvHub,
_28
address improvRelayer,
_28
address bidToken,
_28
address arbitrationNFT,
_28
address wormholeCoreBridge,
_28
address wormholeTokenBridge,
_28
uint16 currentChainId,
_28
uint8 wrappedTokenDecimals
_28
) public initializer {
_28
__Ownable_init();
_28
__UUPSUpgradeable_init();
_28
IMPROV_HUB = improvHub;
_28
IMPROV_RELAYER = improvRelayer;
_28
BID_TOKEN = bidToken;
_28
ARBITRATION_NFT = arbitrationNFT;
_28
WORMHOLE_CORE_BRIDGE = wormholeCoreBridge;
_28
WORMHOLE_TOKEN_BRIDGE = wormholeTokenBridge;
_28
CURRENT_CHAIN_ID = currentChainId;
_28
WRAPPED_BID_TOKEN_DECIMALS = wrappedTokenDecimals;
_28
(, bytes memory queried_decimals) = address(BID_TOKEN).staticcall(
_28
abi.encodeWithSignature('decimals()')
_28
);
_28
BID_TOKEN_DECIMALS = abi.decode(queried_decimals, (uint8));
_28
_28
// the type of tokens on terra is uint128, so its decimals must be smaller.
_28
require(WRAPPED_BID_TOKEN_DECIMALS <= BID_TOKEN_DECIMALS);
_28
}

submit_bid

An NFT holder in any whitelisted collection can bid on a cast position with their NFT. At the end of the bidding process, the highest bidding NFT fills the cast position.

ImprovAuction.sol
Copy

_83
function submit_bid(
_83
uint64 proposalId,
_83
uint16 characterId,
_83
address NFTAddress,
_83
uint256 tokenId,
_83
uint256 bidAmount,
_83
bytes memory proposalVAA
_83
) external {
_83
require(
_83
IERC721(NFTAddress).ownerOf(tokenId) == msg.sender,
_83
'not the NFT owner'
_83
);
_83
// check whether the proposal exists on this chain.
_83
if (!is_proposal_exist(proposalId)) {
_83
require(proposalVAA.length > 0, 'missing VAA');
_83
parse_proposal_payload(parse_vaa(proposalVAA));
_83
}
_83
require(is_proposal_exist(proposalId), 'Invalid VAA');
_83
_83
// check whether the auction has ended.
_83
require(
_83
block.timestamp <= bidding_deadline[proposalId],
_83
'the auction has ended'
_83
);
_83
_83
// check whether the character exists on this chain.
_83
require(
_83
is_character_exist(proposalId, characterId),
_83
'the character does not exist'
_83
);
_83
_83
CastPosition memory cast = cast_positions[proposalId][characterId];
_83
_83
// check whether the bid NFT is valid.
_83
require(
_83
cast.cast_type == CAST_POSITION_NO_LIMIT ||
_83
(cast.cast_type == CAST_POSITION_HAS_LIMIT &&
_83
collections[proposalId][characterId][NFTAddress]),
_83
'the NFT is not in the candidate list'
_83
);
_83
_83
// check whether the bid amount is valid and
_83
// return funds of the last bidder.
_83
if (!is_bid_exist(proposalId, characterId)) {
_83
// if there's no previous bidder.
_83
require(bidAmount >= cast.reserve_price, 'bid price is too small');
_83
require(
_83
(bidAmount - cast.reserve_price) % cast.increment == 0,
_83
'the difference of bid_price should be a multiple of increment'
_83
);
_83
bidded_characters[proposalId].push(characterId);
_83
bidder_count[proposalId] += 1;
_83
total_bid_amount[proposalId] += bidAmount;
_83
} else {
_83
// if there's at least one previous bidder.
_83
Bid memory previous_bid = highest_bids[proposalId][characterId];
_83
require(
_83
bidAmount >= previous_bid.bid_amount + cast.increment,
_83
'bid price is too small'
_83
);
_83
require(
_83
(bidAmount - previous_bid.bid_amount) % cast.increment == 0,
_83
'the difference of bid_price should be a multiple of increment'
_83
);
_83
SafeERC20.safeTransfer(
_83
IERC20(BID_TOKEN),
_83
previous_bid.bidder,
_83
previous_bid.bid_amount
_83
);
_83
total_bid_amount[proposalId] += bidAmount - previous_bid.bid_amount;
_83
}
_83
_83
// update the highest bid.
_83
Bid memory new_bid = Bid(bidAmount, NFTAddress, tokenId, msg.sender);
_83
highest_bids[proposalId][characterId] = new_bid;
_83
_83
SafeERC20.safeTransferFrom(
_83
IERC20(BID_TOKEN),
_83
msg.sender,
_83
address(this),
_83
bidAmount
_83
);
_83
}

submit_veto

submit_veto(proposalId, characterId, startVetoVAA)

When a story is submitted for review, each cast member is issued an arbitration NFT. Cast members can use their arbitration NFTs to veto a story after submission. If enough members decide to veto, the writer must re-submit the story.

ImprovAuction.sol
Copy

_46
function submit_veto(
_46
uint64 proposalId,
_46
uint16 characterId,
_46
bytes memory startVetoVAA
_46
) external {
_46
uint256 token_id = to_unique_id(proposalId, characterId);
_46
require(
_46
IERC721(ARBITRATION_NFT).ownerOf(token_id) == msg.sender,
_46
'not the arbitration NFT owner'
_46
);
_46
_46
if (startVetoVAA.length > 0) {
_46
(
_46
uint64 proposal_id,
_46
uint64 id,
_46
uint64 deadline
_46
) = parse_start_veto_payload(parse_vaa(startVetoVAA));
_46
_46
require(proposalId == proposal_id, 'invalid startVetoVAA');
_46
_46
if (id > latest_veto_id[proposal_id]) {
_46
latest_veto_id[proposal_id] = id;
_46
voting_deadline[proposal_id][id] = deadline;
_46
}
_46
}
_46
_46
// check whether there's at least one veto exists.
_46
require(is_veto_exist(proposalId), 'veto does not exist');
_46
_46
uint64 veto_id = latest_veto_id[proposalId];
_46
_46
// check whether the veto has ended.
_46
require(
_46
block.timestamp <= voting_deadline[proposalId][veto_id],
_46
'the veto has ended'
_46
);
_46
_46
// check whether the character has already been vetoed.
_46
require(
_46
!is_vetoed[proposalId][veto_id][characterId],
_46
'the character has already been vetoed'
_46
);
_46
_46
is_vetoed[proposalId][veto_id][characterId] = true;
_46
vetoed_characters[proposalId][veto_id].push(characterId);
_46
}

Initialize

  • function initialize() initializes the auction contract.
  • wrappedTokenDecimals: the decimals of the Wormhole-wrapped bid token on Terra.
  • Terra tokens are uint128, so BID_TOKEN_DECIMALS decimals must be smaller than require(WRAPPED_BID_TOKEN_DECIMALS.

submit_bid

An NFT holder in any whitelisted collection can bid on a cast position with their NFT. At the end of the bidding process, the highest bidding NFT fills the cast position.

submit_veto

submit_veto(proposalId, characterId, startVetoVAA)

When a story is submitted for review, each cast member is issued an arbitration NFT. Cast members can use their arbitration NFTs to veto a story after submission. If enough members decide to veto, the writer must re-submit the story.

ImprovAuction.sol
CopyExpandClose

_28
function initialize(
_28
bytes32 improvHub,
_28
address improvRelayer,
_28
address bidToken,
_28
address arbitrationNFT,
_28
address wormholeCoreBridge,
_28
address wormholeTokenBridge,
_28
uint16 currentChainId,
_28
uint8 wrappedTokenDecimals
_28
) public initializer {
_28
__Ownable_init();
_28
__UUPSUpgradeable_init();
_28
IMPROV_HUB = improvHub;
_28
IMPROV_RELAYER = improvRelayer;
_28
BID_TOKEN = bidToken;
_28
ARBITRATION_NFT = arbitrationNFT;
_28
WORMHOLE_CORE_BRIDGE = wormholeCoreBridge;
_28
WORMHOLE_TOKEN_BRIDGE = wormholeTokenBridge;
_28
CURRENT_CHAIN_ID = currentChainId;
_28
WRAPPED_BID_TOKEN_DECIMALS = wrappedTokenDecimals;
_28
(, bytes memory queried_decimals) = address(BID_TOKEN).staticcall(
_28
abi.encodeWithSignature('decimals()')
_28
);
_28
BID_TOKEN_DECIMALS = abi.decode(queried_decimals, (uint8));
_28
_28
// the type of tokens on terra is uint128, so its decimals must be smaller.
_28
require(WRAPPED_BID_TOKEN_DECIMALS <= BID_TOKEN_DECIMALS);
_28
}