Skip to main content

Using Derivatives

For a primer on derivatives, read the Concepts - Derivatives page.

This guide will provide an example of how to interact with an auction that utilises an Axis derivative module. Any payout (curator fees, purchases or winning bids) will use the specified derivative module to mint derivative tokens to the recipient. In turn, when the conditions are fulfilled, the recipient will be able to redeem the derivative token for the underlying payout (the auction's base token).

In this example, the LinearVesting derivative module will be used.

To simplify the example, an atomic auction will be used. However, derivative modules work with both atomic and batch auctions.

Auction Creation

Setup

First, we define a variable for the AuctionHouse. The address used here is a testnet address. However, the current addresses for both testnet and production deployments can be obtained from the Contract Addresses page.

// Define the deployed AuctionHouse
IAtomicAuctionHouse auctionHouse = IAtomicAuctionHouse(_atomicAuctionHouse);

The next step is to set up the tokens that will be used in the auction. In this example, mock ERC20 contracts are deployed to make minting simple.

// Define the tokens used in the auction
MockERC20 quoteToken = _getQuoteToken();
MockERC20 baseToken = _getBaseToken();

Inputs

In this section, we prepare the inputs to the auction create function.

Derivative Parameters

The parameters for the derivative module will be included in the routing parameters at the time of auction creation. The derivative parameters therefore need to be prepared first.

// Prepare the parameters for the linear vesting derivative module
uint48 vestingStart = uint48(block.timestamp + 7 days);
uint48 vestingExpiry = vestingStart + 90 days;
ILinearVesting.VestingParams memory vestingParams =
ILinearVesting.VestingParams({start: vestingStart, expiry: vestingExpiry});

These parameters will result in the auction payout to the curator (if applicable) and buyers:

  • Commencing vesting 7 days from the time of auction creation
  • Linearly vesting over 90 days from the vesting start time

Routing Parameters

The Routing parameters are then created. In this code block, the derivativeType and derivativeParams are specified, unlike in the Create Auction Guide.

// Define the auction routing parameters
IAuctionHouse.RoutingParams memory routingParams = IAuctionHouse.RoutingParams({
auctionType: toKeycode("EMPA"),
baseToken: address(baseToken),
quoteToken: address(quoteToken),
curator: address(0), // Optional
callbacks: ICallback(address(0)), // Optional
callbackData: abi.encode(""), // Optional
derivativeType: toKeycode("LIV"), // Linear vesting
derivativeParams: abi.encode(vestingParams), // Linear vesting parameters
wrapDerivative: true // true to wrap derivative tokens in an ERC20
});

Note that once an auction is created, the derivative configuration cannot be modified.

Auction Parameters

The configuration of the FixedPriceSale auction module is then created.

// Define the auction module parameters
IFixedPriceSale.AuctionDataParams memory fpsParams;
{
uint256 fpsPrice = 1e18;
uint24 maxPayoutPercent = 10_000; // 10000 = 10%
fpsParams = IFixedPriceSale.AuctionDataParams({
price: fpsPrice,
maxPayoutPercent: maxPayoutPercent
});
}

The FixedPriceSale configuration is then encoded in the generic auction parameters:

// Define the auction parameters
IAuction.AuctionParams memory auctionParams;
uint256 capacity;
{
uint48 start = uint48(block.timestamp + 1 days);
uint48 duration = uint48(3 days);
bool capacityInQuote = false;
capacity = 10e18;

auctionParams = IAuction.AuctionParams({
start: start,
duration: duration,
capacityInQuote: capacityInQuote,
capacity: capacity,
implParams: abi.encode(fpsParams)
});
}

Token Funding

While the atomic auction is not pre-funded, the base tokens need to be available for spending by the AtomicAuctionHouse.

// Mint base tokens to the seller
baseToken.mint(_SELLER, capacity);

// The AuctionHouse will pull base tokens from the seller upon purchase
// so approve the auction capacity
vm.prank(_SELLER);
baseToken.approve(_atomicAuctionHouse, capacity);

Contract Call

Lastly, we create the auction. The caller of the contract is considered the seller, so we use vm.prank to assume the role of the seller in the example script.

// Define the IPFS hash for additional information
string memory ipfsHash = "";

// Create the auction
vm.prank(_SELLER);
uint96 lotId = auctionHouse.auction(routingParams, auctionParams, ipfsHash);
console2.log("Created auction with lot ID:", lotId);

Purchase

In order to obtain the derivative token as a payout from the atomic auction, a purchase must be performed. This is identical to what is contained in the Purchase Guide.

However, as the auction is configured with a derivative module, the purchase will result in the derivative token being minted to the buyer, instead of the auction's base token. Due to wrapDerivative being true, the derivative will take the form of an ERC20.

Derivative Redemption

The next stage is to redeem the derivative token for the underlying token (the auction base token). The example script will attempt to redeem the maximum amount of base tokens that can be redeemed.

Setup

The sample script isolates the code for redemption into its own function, so a variable must be created for the AtomicAuctionHouse.

IAtomicAuctionHouse auctionHouse = IAtomicAuctionHouse(_atomicAuctionHouse);

We obtain from the LOT_ID environment variable the lot ID that identifies each auction lot. The lot ID is a uint96 number, so should be checked before being downcast.

// Obtain the lot from the environment variable
uint256 lotIdRaw = vm.envUint("LOT_ID");
if (lotIdRaw > type(uint96).max) {
revert("LOT_ID must be less than uint96 max");
}
uint96 lotId = uint96(lotIdRaw);

Inputs

We define a variable for the purchase recipient, which is the same as what was defined in the purchase step.

// Prepare inputs
address recipient = address(0x10);

As we will be accessing the derivative module, the address should be determined and cached.

// Obtain the address of the derivative module
IDerivative derivativeModule = auctionHouse.getDerivativeModuleForId(lotId);

The unique tokenId is required in order to determine the amount that can be redeemed. The unique value is determined based on the underlying token (the auction's base token) and the vesting parameters.

// Determine the token id
(, address baseToken,,,,,,, bytes memory derivativeParams) = auctionHouse.lotRouting(lotId);
uint256 tokenId = derivativeModule.computeId(baseToken, derivativeParams);

Contract Call

For logging purposes, we then call the redeemable() view function to determine how much can be redeemed at the current timestamp.

// Determine how much can be redeemed
uint256 redeemable = derivativeModule.redeemable(recipient, tokenId);
console2.log("Redeemable amount:", redeemable);

Lastly, the redeemMax() function is called to perform the redemption.

// Redeem the maximum amount
// Must be run as the recipient
vm.prank(recipient);
derivativeModule.redeemMax(tokenId);
console2.log("Redeemed maximum amount");

This will result in:

  • redeemable quantity of the derivative token being burned by the derivative module
  • redeemable quantity of the auction's base token being transferred to the recipient

Source Code

The source code for the guide is located in the using-derivatives.s.sol file.