Time-Weighted Average Price
This smart contract allows the incentivized execution of a TWAP order (either a Limit Order or a Market Order) on any DEX, with the possibility of partial fills. A TWAP order breaks a larger order down into smaller trades or "chunks", which are executed over a set period of time. This is a common strategy in traditional finance but it was not previously possible to execute such trades in a decentralized manner in DeFi systems.
In this smart contract, users (makers) create orders that wait in the contract to be filled. Once made, these orders enable an English Auction bidding war on each chunk at its time interval. Anyone willing to participate can serve as a βtakerβ by finding the best path to fill the order for the next chunk on any DEX, within the parameters set by the maker. Takers submit these paths as a bid to the contract, which selects the winner based on criteria described in detail below.
The winning taker receives a portion of the output tokens as a reward for their effort.
One honest taker (i.e., a taker who is willing to set the fee at the minimum amount needed to cover gas costs) is enough to ensure the entire system functions effectively at spot prices.
The contract is set to operate only up to the year 2106 (32bit timestamps), at which point it will no longer be usable.
The
TWAP
Smart Contract does not hold any funds, has no owners, administrators, or other roles and is entirely immutable once deployed on an EVM blockchain.
TWAP has several major benefits for users and trading platforms alike.
TWAP orders can limit the disproportionate price impact that often occurs when making large orders or long-tail low liquidity token pairs, especially on DEX/AMMs where liquidity is often fragmented among pools and prices are volatile.
Users should take into account the following considerations when using TWAP orders for this purpose:
maker
, as takers will only be willing to bid if they can economically benefit from filling the transaction,dstMinAmount
) may also be only partially filled if price moves away while the order is in flight.Dollar-cost averaging (DCA) is an investing strategy where the investor purchases an asset or set of assets having a certain dollar value on a specific schedule (i.e., on the first day of every month). TWAP trades can be used to generate an automated version of this strategy.
Users should take the following into account when using the TWAP contract for this purpose:
0 dstMinAmount
) and a long fill delay will create a bidding war on the next chunk on a periodic basis (can bemaker
: User, the Order creator. In order to execute orders, is required to have an input token (srcToken
) balance and provide an approval to be swapped by the TWAP
exchange
.
taker
: Incentivized independant participators that monitor Orders submitted by makers.
fee
for the taker.dstFee
: 0 or more of the dstToken
output (i.e., the token the maker
is purchasing) to be sent to the taker
at the time the chunk is filled.Orders
.Before an Order can be filled, makers sign a transaction giving approval to the TWAP contract to remove the applicable input asset from their wallet.
maker
, who, after granting the approval, sends an ask
transaction to the TWAP contract containing certain requested parameters (specified below).Takers monitor the Orders held by the contract, and can send bid transactions specifying the DEX on which it proposes to execute the next chunk, the output amount it can receive and the requested fee.
As a result of this process, an Order
is generated and held in the TWAP
contract, which contains the following parameters and constraints:
id
: the index in the order book, generatedstatus
: canceled, completed, or deadlinefilledTime
: last chunk filled timestampsrcFilledAmount
: total filled amount in srcToken
Ask
: holds the order parameters requested by the maker in the ask
transaction
time
: order creation timestampdeadline
: order duration timestamp, requiredbidDelay
: minimum delay in seconds before a bid can be filled, must be >=MIN_BID_DELAY_SECONDS
fillDelay
: minimum delay in seconds between chunksmaker
: order creator (msg.sender
)exchange
: swap only on this exchange, or zero for any exchangesrcToken
: input token, requireddstToken
: output token, required. If zero address, will unwrap to native token on each chunk.srcAmount
: input total order amount in srcToken
, requiredsrcBidAmount
: input chunk size in srcToken
, requireddstMinAmount
: minimum output chunk size, in dstToken
, required. Can be higher than market output (implies a limit order), or as low as 1 weiBid
: empty until the first bid
received. Once bids begin to be received, holds the current winning bid information:
time
: bid creation timestamptaker
: the winning bidderexchange
: execute bid on this exchange, never zero on a valid biddstAmount
: output amount for this bid after fees in dstToken
dstFee
: requested by taker
for performing the bid and fill, in dstToken
, may be 0
data
: swap data passed to exchange, expected output = dstAmount
+ dstFee
allowance
to swap the srcToken
, with the approval covering an amount that is high enough to fill the next chunk.balance
of the srcToken
to be swapped (for the next chunk).fillDelay
is set by the order maker, minimum MIN_FILL_DELAY_SECONDS
exchange
would be utilized for the order, only that exchange
can be used to swap. If the value is set to zero
, any exchange can be used.MAX_BID_WINDOW_SECONDS
will be cleared and removed.Any invalid constraint will revert the bid
transaction, so a successful transaction implies a win
This delay gives other bidders an opportunity to make a competing bid, allowing for a bidding war on the applicable chunk.
MIN_BID_WINDOW_SECONDS
interval to challenge it.taker
(winning bidder) can fill the bid by calling fill(id)
.fill
transaction, the TWAP
contract performs the same verifications as when bidding.fill
transaction also utilizes the allowance previously provided by the maker to perform the actual swap on the requested exchange, resulting in thedstFee
is set >0
, the specified fee is paid out to the winning taker as part of the completion of the order, out of the dstToken
amount of that swap.fill
transaction.This is because this honest taker, by charging a lower fee, will propose a transaction containing the highest output to the maker and will therefore be selected as the winning bid when competing with those charging higher fees, all other things equal. Therefore, if the honest bidder is equally capable of locating the best path as any competing taker, the price received by the maker will be optimal.
Please note the following additional features of the TWAP contract:
srcToken
for each chunk before that chunk is available for bidding. It does not have topruned
(canceled), by anyone..env
file with (or pass as environment variables):NETWORK_URL_ETH="https://eth-mainnet.g.alchemy.com/v2/08***************************Kf"
NETWORK_URL_POLY="https://polygon-mainnet.g.alchemy.com/v2/a7***************************xN"
ETHERSCAN_ETH="VV***************************14"
ETHERSCAN_POLY="9H***************************WY"
COINMARKETCAP="81***************************36"
forge test