# Oracle Sequential Dutch Auctioneer (OSDA)

The Oracle Sequential Dutch Auctioneer implements a simplified sequential dutch auction pricing methodology which seeks to sell out the capacity of the market linearly over the duration. We do so by implementing a linear decay of price based on the percent difference in expected capacity vs. actual capacity (relative to the initial capacity) at any given point in time. The OSDA allows specifying a base discount from the oracle price and calculates a decay speed based on a target deposit interval and target discount over that interval. More specifically, we define the price as:

$$P(t) = O(t) \times (1 - b) \times (1 + k \times r(t))$$

where $$O(t)$$ is the oracle price at time $$t$$, $$b$$ is the base discount percent of the market, $$k$$ is the decay speed, and $$r(t)$$ is the capacity ratio.

We calculate $$k$$ on market creation as:

$$k = \frac{L}{I\_d} \times d$$

where $$L$$ is the duration of the market, $$I\_d$$ is the deposit interval, and $$d$$ is the target discount over a deposit interval.

We calculate the capacity ratio as:

$$r(t) = \frac{\chi(t) - C(t)}{C\_0}$$

where $$C\_0$$ is the initial capacity of the market, $$C(t)$$ is the remaining capacity at time $$t$$, and $$\chi(t) = C\_0 \times \frac{L - t}{L}$$ is the expected capacity at time $$t$$.

Market creators can also set a minimum total discount from the starting price, which creates a hard floor for the market price.

The below chart shows a notional example of how price might evolve over an OSDA market.

<figure><img src="/files/7p9clNdTH9kAxYXlmZQS" alt=""><figcaption><p>Notional Price of an OSDA Market over Time</p></figcaption></figure>

If you're familiar with other dutch auction mechanism designs, this version of the SDA is similar to [Paradigm's Continuous Gradual Dutch Auction (GDA) model](https://www.paradigm.xyz/2022/04/gda#continuous-gda) with linear decay, but there is no price slippage based on the purchase amount. Note the version described in the link above uses exponential decay vs. linear decay, but it's possible to derive a linear decay version as well.

Configuring appropriate base discount and target interval discounts is a function of the market demand for the token and the vesting period of the payouts. Typically, longer vesting periods require a larger base discount. Target interval discounts may need to be higher for smaller cap tokens or where demand is soft. However, various combinations can be used depending on desired market characteristics. For example, a high base discount and low target interval discount will give a consistently good deal with low volatility in the market price. The opposite configuration, low base discount and high target interval discount, will be more volatile, but may result in lower overall discounts depending on demand.

## Data Structures (Structs)

### MarketParams

Parameters to create a new OSDA market. Encoded as bytes and provided as input to `createMarket`.

```solidity
struct MarketParams {
    ERC20 payoutToken;
    ERC20 quoteToken;
    address callbackAddr;
    IBondOracle oracle;
    uint48 baseDiscount;
    uint48 maxDiscountFromCurrent;
    uint48 targetIntervalDiscount;
    bool capacityInQuote;
    uint256 capacity;
    uint48 depositInterval;
    uint48 vesting;
    uint48 start;
    uint48 duration;
}
```

| Field                  | Type                  | Description                                                                                                                                                                                                                                                    |
| ---------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| payoutToken            | ERC20 (address)       | Payout Token (token paid out by market and provided by creator)                                                                                                                                                                                                |
| quoteToken             | ERC20 (address)       | Quote Token (token to be received by the market and provided by purchaser)                                                                                                                                                                                     |
| callbackAddr           | address               | Callback contract address, should conform to `IBondCallback`. If 0x00, tokens will be transferred from market owner. Using a callback requires whitelisting by the protocol                                                                                    |
| oracle                 | IBondOracle (address) | Oracle contract address, must conform to `IBondOracle`. Provides current price data for the quote/payout token pair.                                                                                                                                           |
| baseDiscount           | uint48                | Base discount from oracle price before time-related decay is applied. Units are a percent with 3 decimals of precision (e.g. `10_000` = 10%)                                                                                                                   |
| maxDiscountFromCurrent | uint48                | Max Discount (%) from the oracle price when the time the market is created. Sets a minimum price for the market. Units are a percent with 3 decimals of precision (e.g. `10_000` = 10%).                                                                       |
| targetIntervalDiscount | uint48                | The discount from zero to be achieved over a depositInterval if no purchases are made during that time. Controls the decay speed of the market price. Applied after the base  discount. Units are a percent with 3 decimals of precision (e.g. `10_000` = 10%) |
| capacityInQuote        | bool                  | Is Capacity in Quote Token?                                                                                                                                                                                                                                    |
| capacity               | uint256               | Capacity (amount in quote token decimals or amount in payout token decimals). Set `capacityInQuote` flag appropriately.                                                                                                                                        |
| depositInterval        | uint48                | Target deposit interval for purchases (in seconds). Determines the `maxPayout` of the market. Minimum is 1 hour (3600 seconds)                                                                                                                                 |
| vesting                | uint48                | Is fixed term ? Vesting length (seconds) : Vesting expiry (timestamp). A 'vesting' param longer than 50 years is considered a timestamp for fixed expiry                                                                                                       |
| start                  | uint48                | Start time of the market as a timestamp. Allows starting a market in the future. If provided, the transaction must be sent prior to the start time. If not provided, the market will start immediately.                                                        |
| duration               | uint48                | Duration of the market in seconds.                                                                                                                                                                                                                             |

### BondMarket

BondMarket contains the token, callback, capacity, owner, and purchased/sold amount data for a Oracle Fixed Discount Market.

```solidity
struct BondMarket {
    address owner; // market owner. sends payout tokens, receives quote tokens (defaults to creator)
    ERC20 payoutToken; // token to pay depositors with
    ERC20 quoteToken; // token to accept as payment
    address callbackAddr; // address to call for any operations on bond purchase. Must inherit to IBondCallback.
    bool capacityInQuote; // capacity limit is in payment token (true) or in payout (false, default)
    uint256 capacity; // capacity remaining
    uint256 maxPayout; // max payout tokens out in one order
    uint256 sold; // payout tokens out
    uint256 purchased; // quote tokens in
}
```

### BondTerms

BondTerms contains the oracle, pricing, and time parameters of an Oracle Fixed Discount Market.

```solidity
struct BondTerms {
    IBondOracle oracle; // address to call for reference price. Must implement IBondOracle.
    uint48 start; // timestamp when market starts
    uint48 conclusion; // timestamp when market no longer offered
    uint48 vesting; // length of time from deposit to expiry if fixed-term, vesting timestamp if fixed-expiry
    uint48 baseDiscount; // base discount from oracle price, with 3 decimals of precision. E.g. 10_000 = 10%
    uint48 decaySpeed; // market price decay speed (discount achieved over a target deposit interval)
    uint256 minPrice; // minimum price (hard floor for the market)
    uint256 scale; // scaling factor for the market (see MarketParams struct)
    uint256 oracleConversion; // conversion factor for oracle -> market price
}
```

## Public Variables and View Methods

### allowNewMarkets

```solidity
function allowNewMarkets() external view returns (bool)
```

Whether or not the auctioneer allows new markets to be created

*Changing to false will sunset the auctioneer after all active markets end*

### authority

```solidity
function authority() external view returns (contract Authority)
```

Roles authority contract for the bond system which managed access-control to permissioned functions.

### callbackAuthorized

```solidity
function callbackAuthorized(address) external view returns (bool)
```

Whether or not the market creator is authorized to use a callback address

#### Parameters

| Name      | Type    | Description             |
| --------- | ------- | ----------------------- |
| creator\_ | address | Market creator address. |

### currentCapacity

```solidity
function currentCapacity(uint256 id_) external view returns (uint256)
```

Returns current capacity of a market

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### getAggregator

```solidity
function getAggregator() external view returns (contract IBondAggregator)
```

Returns the Aggregator that services the Auctioneer

### getMarketInfoForPurchase

```solidity
function getMarketInfoForPurchase(uint256 id_) external view returns (address owner, address callbackAddr, contract ERC20 payoutToken, contract ERC20 quoteToken, uint48 vesting, uint256 maxPayout_)
```

Provides information for the Teller to execute purchases on a Market

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

#### Returns

| Name         | Type           | Description                                                                       |
| ------------ | -------------- | --------------------------------------------------------------------------------- |
| owner        | address        | Address of the market owner (tokens transferred from this address if no callback) |
| callbackAddr | address        | Address of the callback contract to get tokens for payouts                        |
| payoutToken  | contract ERC20 | Payout Token (token paid out) for the Market                                      |
| quoteToken   | contract ERC20 | Quote Token (token received) for the Market                                       |
| vesting      | uint48         | Timestamp or duration for vesting, implementation-dependent                       |
| maxPayout\_  | uint256        | Maximum amount of payout tokens you can purchase in one transaction               |

### getTeller

```solidity
function getTeller() external view returns (contract IBondTeller)
```

Returns the Teller that services the Auctioneer

### isInstantSwap

```solidity
function isInstantSwap(uint256 id_) external view returns (bool)
```

Does market send payout immediately

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### isLive

```solidity
function isLive(uint256 id_) external view returns (bool)
```

Is a given market accepting deposits

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### marketPrice

```solidity
function marketPrice(uint256 id_) external view returns (uint256)
```

Calculate current market price. Value returned is the number of quote tokens per payout token. The value is the oracle price scaled by the [#bondterms](#bondterms "mention").oracleConversion factor. `marketPrice` and `marketScale` can be used to convert between amounts of quote tokens and payout tokens at the current price.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### marketScale

```solidity
function marketScale(uint256 id_) external view returns (uint256)
```

Scale value to use when converting between quote token and payout token amounts with marketPrice()

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### markets

```solidity
function markets(uint256) external view returns (address owner, contract ERC20 payoutToken, contract ERC20 quoteToken, address callbackAddr, bool capacityInQuote, uint256 capacity, uint256 maxPayout, uint256 sold, uint256 purchased)
```

Returns the token, callback, capacity, owner, and purchased/sold amount data for a market. See [#bondmarket](#bondmarket "mention").

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### maxAmountAccepted

```solidity
function maxAmountAccepted(uint256 id_, address referrer_) external view returns (uint256)
```

Returns maximum amount of quote token accepted by the market

**Parameters**

| Name       | Type    | Description                                                                                                                                         |
| ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| id\_       | uint256 | Market ID                                                                                                                                           |
| referrer\_ | address | Address of referrer, used to get fees to calculate accurate payout amount. Inputting the zero address will take into account just the protocol fee. |

### maxPayout

```solidity
function maxPayout(uint256 id_) external view returns (uint256)
```

Calculate max payout of the market in payout tokens

*Returns a dynamically calculated payout or the maximum set by the creator, whichever is less. If the remaining capacity is less than the max payout, then that amount will be returned.*

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### minDepositInterval

```solidity
function minDepositInterval() external view returns (uint48)
```

Minimum deposit interval for a market

### minMarketDuration

```solidity
function minMarketDuration() external view returns (uint48)
```

Minimum market duration in seconds

### newOwners

```solidity
function newOwners(uint256) external view returns (address)
```

New address to designate as market owner. They must accept ownership to transfer permissions.

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### ownerOf

```solidity
function ownerOf(uint256 id_) external view returns (address)
```

Returns the address of the market owner

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### payoutFor

```solidity
function payoutFor(uint256 amount_, uint256 id_, address referrer_) external view returns (uint256)
```

Payout due for amount of quote tokens at current market price

**Parameters**

| Name       | Type    | Description                                                                                                                                         |
| ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| amount\_   | uint256 | Amount of quote tokens to spend                                                                                                                     |
| id\_       | uint256 | Market ID                                                                                                                                           |
| referrer\_ | address | Address of referrer, used to get fees to calculate accurate payout amount. Inputting the zero address will take into account just the protocol fee. |

### terms

```solidity
function terms(uint256) external view returns (contract IBondOracle oracle, uint48 start, uint48 conclusion, uint48 vesting, uint48 fixedDiscount, uint256 minPrice, uint256 scale, uint256 oracleConversion)
```

Information pertaining to oracle, pricing, and time parameters. See [#bondterms](#bondterms "mention").

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

## State-Mutating Methods

### closeMarket

```solidity
function closeMarket(uint256 id_) external nonpayable
```

Disable existing bond market. Must be market owner

**Parameters**

| Name | Type    | Description |
| ---- | ------- | ----------- |
| id\_ | uint256 | Market ID   |

### createMarket

```solidity
function createMarket(bytes params_) external nonpayable returns (uint256)
```

Creates a new bond market

*See* [#marketparams](#marketparams "mention") for the required formatting for the abi-encoded input params.

**Parameters**

| Name     | Type  | Description                                                             |
| -------- | ----- | ----------------------------------------------------------------------- |
| params\_ | bytes | Configuration data needed for market creation, encoded in a bytes array |

#### Returns

|      |         |           |
| ---- | ------- | --------- |
| id\_ | uint256 | Market ID |

### pullOwnership

```solidity
function pullOwnership(uint256 id_) external nonpayable
```

Accept ownership of a marketMust be market newOwner

*The existing owner must call pushOwnership prior to the newOwner calling this function*

**Parameters**

|      |         |           |
| ---- | ------- | --------- |
| id\_ | uint256 | Market ID |

### purchaseBond

```solidity
function purchaseBond(uint256 id_, uint256 amount_, uint256 minAmountOut_) external nonpayable returns (uint256 payout)
```

Exchange quote tokens for a bond in a specified market. Must be teller. End users interact with the Teller contract to purchase.

**Parameters**

| Name           | Type    | Description                                                          |
| -------------- | ------- | -------------------------------------------------------------------- |
| id\_           | uint256 | Market ID                                                            |
| amount\_       | uint256 | Amount to deposit in exchange for bond (after fee has been deducted) |
| minAmountOut\_ | uint256 | Minimum acceptable amount of bond to receive. Prevents front-running |

#### Returns

| Name   | Type    | Description                                         |
| ------ | ------- | --------------------------------------------------- |
| payout | uint256 | Amount of payout token to be received from the bond |

### pushOwnership

```solidity
function pushOwnership(uint256 id_, address newOwner_) external nonpayable
```

Designate a new owner of a marketMust be market owner

*Doesn't change permissions until newOwner calls pullOwnership*

**Parameters**

| Name       | Type    | Description                      |
| ---------- | ------- | -------------------------------- |
| id\_       | uint256 | Market ID                        |
| newOwner\_ | address | New address to give ownership to |

### setAllowNewMarkets

```solidity
function setAllowNewMarkets(bool status_) external nonpayable
```

Change the status of the auctioneer to allow creation of new markets

*Setting to false and allowing active markets to end will sunset the auctioneer*

**Parameters**

| Name     | Type | Description                                                     |
| -------- | ---- | --------------------------------------------------------------- |
| status\_ | bool | Allow market creation (true) : Disallow market creation (false) |

### setCallbackAuthStatus

```solidity
function setCallbackAuthStatus(address creator_, bool status_) external nonpayable
```

Change whether a market creator is allowed to use a callback address in their markets or not. Must be guardian

*Callback is believed to be safe, but a whitelist is implemented to prevent abuse*

**Parameters**

| Name      | Type    | Description                                       |
| --------- | ------- | ------------------------------------------------- |
| creator\_ | address | Address of market creator                         |
| status\_  | bool    | Allow callback (true) : Disallow callback (false) |

### setMinDepositInterval

```solidity
function setMinDepositInterval(uint48 depositInterval_) external nonpayable
```

Set the minimum deposit interval. Access controlled

**Parameters**

| Name              | Type   | Description                         |
| ----------------- | ------ | ----------------------------------- |
| depositInterval\_ | uint48 | Minimum deposit interval in seconds |

### setMinMarketDuration

```solidity
function setMinMarketDuration(uint48 duration_) external nonpayable
```

Set the minimum market duration. Access controlled

**Parameters**

| Name       | Type   | Description                        |
| ---------- | ------ | ---------------------------------- |
| duration\_ | uint48 | Minimum market duration in seconds |

## Events

### MarketClosed

```solidity
event MarketClosed(uint256 indexed id)
```

**Parameters**

| Name         | Type    | Description |
| ------------ | ------- | ----------- |
| id `indexed` | uint256 | Market ID   |

### MarketCreated

```solidity
event MarketCreated(uint256 indexed id, address indexed payoutToken, address indexed quoteToken, uint48 vesting)
```

**Parameters**

| Name                  | Type    | Description                   |
| --------------------- | ------- | ----------------------------- |
| id `indexed`          | uint256 | Market ID                     |
| payoutToken `indexed` | address | Address of the payout token   |
| quoteToken `indexed`  | address | Address of the quote token    |
| vesting               | uint48  | Vesting duration or timestamp |

## Errors

The following custom errors are used in the OSDA contract to denote specific revert conditions. The errors are prefixed with "Auctioneer" to denote that it is within this contract when multi-contract actions are performed (such as buying a bond).

```solidity
error Auctioneer_OnlyMarketOwner();
error Auctioneer_InitialPriceLessThanMin();
error Auctioneer_MarketNotActive();
error Auctioneer_MaxPayoutExceeded();
error Auctioneer_AmountLessThanMinimum();
error Auctioneer_NotEnoughCapacity();
error Auctioneer_InvalidCallback();
error Auctioneer_BadExpiry();
error Auctioneer_InvalidParams();
error Auctioneer_NotAuthorized();
error Auctioneer_NewMarketsNotAllowed();
error Auctioneer_OraclePriceZero();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.bondprotocol.finance/smart-contracts/bond-system/auctioneer/oracle-sequential-dutch-auctioneer-osda.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
