Skip to content

Latest commit

 

History

History
856 lines (628 loc) · 70.5 KB

DOCUMENTATION.md

File metadata and controls

856 lines (628 loc) · 70.5 KB

AaveDIVAWrapper - Documentation

This documentation outlines the functionality of the AaveDIVAWrapper contract.

Table of contents

  1. Overview
  2. Addressbook
  3. Terminology
  4. Contract overview
  5. Owner privileges
  6. Upgradeability
  7. Main user flows
  8. Invariants
  9. Function overview
  10. Miscellaneous
  11. Risk disclaimer
  12. Resources

Overview

AaveDIVAWrapper is a smart contract that acts as a connector between DIVA Protocol and Aave V3, allowing assets deposited into DIVA Protocol pools to generate yield by supplying them on Aave V3. The generated yield is claimable by the owner of the AaveDIVAWrapper contract.

The following diagram illustrates the high-level flow of funds. A technically more detailed flow is provided in the Main user flows section.

alt-tag

  1. User deposits funds (e.g., USDC) into AaveDIVAWrapper.
  2. AaveDIVAWrapper supplies these funds to Aave V3 for yield generation.
  3. AaveDIVAWrapper creates a new pool or adds liquidity to an existing pool in DIVA Protocol using a wrapped token (wToken) as collateral.
  4. The yield generated by Aave V3 is claimable by the owner.

This contract was originally designed for DIVA Donate on Arbitrum, a parametric conditional donations platform, which uses the yield to purchase insurance policies to increase donation payouts beyond the users' initial contributions. However, it can be utilized for any other use case enabled by DIVA Protocol (e.g., prediction markets, structured products, etc.). Users building on top of DIVA Protocol can deploy their own AaveDIVAWrapper instance and designate themselves as the yield recipient to receive a sustainable revenue stream.

Example

To demonstrate the functionality of the AaveDIVAWrapper contract, let's compare two scenarios for Alice's contribution to a DIVA Donate campaign: one using direct DIVA Protocol interaction, and another utilizing the AaveDIVAWrapper.

Direct DIVA Protocol interaction

  • Alice contributes 100 USDC via DIVA Protocol through the DIVA Donate platform.
  • When the outcome is reported, 100 USDC either go to the beneficiary (if drought conditions are met) or back to Alice.
  • The 100 USDC sits idle in the pool during the campaign's lifetime; no yield is generated.

Using AaveDIVAWrapper

  • Alice contributes 100 USDC via the AaveDIVAWrapper contract through the DIVA Donate platform.
  • AaveDIVAWrapper supplies the 100 USDC on Aave which generates a yield of say 5 USDC during the campaign's lifetime.
  • The owner of AaveDIVAWrapper can use this generated yield in several ways:
    • Cover operational costs of running the platform.
    • Distribute directly to beneficiaries when donation conditions are met.
    • Purchase insurance policies to amplify potential payouts (e.g., using 5 USDC to buy a policy worth 20 USDC).
    • A combination of the above.
  • If a donation is triggered, the deposited 100 USDC is paid out to the farmer, along with any additional benefits from the yield usage (e.g., insurance payout or direct yield distribution), thereby increasing the impact of the donation. If no donation is triggered, the contributing user can redeem their 100 USDC from AaveDIVAWrapper (ignoring DIVA fees for simplicity).

Additional remarks

  • Supported tokens: The AaveDIVAWrapper accepts any token as collateral that is supported by Aave V3 (e.g., USDC, USDT, etc.) and that was registered in AaveDIVAWrapper by the owner.
  • Fee-on-transfer tokens: As Aave V3 does not support fee-on-transfer tokens (see here), the AaveDIVAWrapper contract does not support them either.
  • EIP-712: As opposed to DIVA Protocol, AaveDIVAWrapper does not support EIP-712 features in the current version. This is a minor limitation though since users can create markets via AaveDIVAWrapper, add liquidity to mint position tokens, and trade these positions on DEXs like 0x protocol that support EIP-712. EIP-712 features are planned for a future release.
  • Aave V3: The AaveDIVAWrapper contract is built on Aave V3, which is an upgradeable protocol. For simplicity, all references to "Aave" or "Aave V3" in this documentation refer specifically to Aave V3.2.
  • Outcome reporting: The oracle reporting process remains unchanged from DIVA Protocol's standard process. Data providers / oracles interact directly with DIVA Protocol and not via the AaveDIVAWrapper contract.

Addressbook

The following table provides an overview of the relevant protocol addresses for each available network and some example collateral tokens that can be used during the initialization of AaveDIVAWrapper.

Arbitrum

Contract name Address
AaveDIVAWrapper to be added after deployment
DIVA Protocol 0x2C9c47E7d254e493f02acfB410864b9a86c28e1D
Aave V3 (Pool contract) 0x794a61358D6845594F94dc1DB02A252b5b4814aD
Example collateral tokens
USDC (native) 0xaf88d065e77c8cC2239327C5EDb3A432268e5831
USDT 0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9

Terminology

Collateral token

The collateral token is the underlying asset (e.g., USDC, USDT) that users deposit into AaveDIVAWrapper. These tokens must be supported by Aave V3 and registered by the owner in AaveDIVAWrapper before they can be used.

Position token

The position token is the token representing the long or short position in a DIVA Protocol pool, minted by DIVA Protocol when users add liquidity to a pool.

AToken

ATokens are interest-bearing tokens that represent deposits in Aave markets. They have two key characteristics:

  1. They are rebaseable tokens, meaning their balances automatically increase over time as interest accrues.
  2. They maintain a 1:1 peg to the underlying asset (e.g., 1 aUSDC = 1 USDC).

When AaveDIVAWrapper supplies assets to an Aave market, it receives aTokens in return. The yield earned on the supplied assets is automatically reflected through an increase in the aToken balance in AaveDIVAWrapper's wallet. For more details, see the Aave documentation.

Wrapped token (wToken)

DIVA Protocol requires pool collateralization via non-rebasable ERC20 tokens to ensure stable pool accounting. Since Aave's aTokens are rebasable, they cannot be used directly as collateral in DIVA Protocol. To solve this, the AaveDIVAWrapper contract creates wrapped versions of collateral tokens (e.g., wUSDC for USDC), so-called wTokens, when the owner registers a collateral token via the registerCollateralToken function that serve as proxy collateral in DIVA Protocol. These wrapped tokens are non-rebasable and redeemable 1:1 for the underlying collateral token, making them suitable for use as collateral in DIVA Protocol.

wTokens represent AaveDIVAWrapper's liability of the underlying asset (e.g., USDC) towards long and short token holders, with the total wToken supply reflecting the contract's total liability. Only the AaveDIVAWrapper contract has the authority to mint and burn wTokens.

While short and long tokens are collateralized with wTokens, they can be priced in the underlying asset (e.g., USDC) on DEXs like 0x protocol due to 1:1 redemption guarantee for the underlying asset. User's that redeem short or long tokens directly from DIVA Protocol for wTokens can always convert them into the underlying asset using AaveDIVAWrapper's redeemWToken function.

The wToken address associated with a collateral token such as USDC or USDT can be retrieved via the getWToken function.

Yield

When an asset like USDC is supplied to Aave, yield accrues as an increase in the AaveDIVAWrapper contract's aUSDC balance. The aUSDC amount in excess of the total wUSDC supply is the generated yield, claimable by the AaveDIVAWrapper contract's owner via the claimYield function.

Contract overview

The main contracts are:

  • IAaveDIVAWrapper: Interface for the AaveDIVAWrapper contract.
  • AaveDIVAWrapperCore: An abstract contract that inherits from IAaveDIVAWrapper and implements the core functions of the AaveDIVAWrapper contract as internal functions.
  • AaveDIVAWrapper: The main contract that inherits from AaveDIVAWrapperCore and implements the core functionality of the AaveDIVAWrapper contract (including batch versions of the core functions) as external functions. That is the contract that users interact with.
  • WToken: The contract that represents a wrapped version of the collateral token. Deployed when the owner registers a collateral token via the registerCollateralToken function.

External contracts

For contract addresses across different networks, refer to the respective address pages of DIVA Protocol and Aave V3. The documentation and codebases for both DIVA Protocol and Aave V3 can be found in the Resources section.

Contract deployment

To deploy the AaveDIVAWrapper contract, the following parameters must be provided:

  1. diva_: Address of the DIVA Protocol contract.
  2. aaveV3_: Address of the Aave V3 contract.
  3. owner_: Address of the owner of the contract, eligible to claim yield and register collateral tokens.

Execution steps

  1. Validate that none of the provided addresses are zero.
  2. Set the DIVA Protocol address (_diva) to the provided diva_ address.
  3. Set the Aave V3 Pool address (_aaveV3Pool) to the provided aaveV3Pool_ address.
  4. Initialize the Ownable contract with the provided owner_ address.

Revert conditions

Error Condition Contract/Library
ZeroAddress When the DIVA Protocol or Aave V3 Pool contract address is zero. AaveDIVAWrapper
OwnableInvalidOwner When the owner_ address is zero. Ownable (OpenZeppelin)

Emitted events

Event Description Contract/Library
OwnershipTransferred Emitted when ownership is set to the initial owner. Ownable2Step (OpenZeppelin)

Owner privileges

  • Register collateral tokens (token must be supported by Aave V3)
  • Claim yield
  • Initiate process to update owner in a 2-step process

Upgradeability

  • AaveDIVAWrapper is immutable and cannot be upgraded.
  • DIVA Protocol is immutable and cannot be upgraded.
  • Aave V3 is upgradeable and has been upgraded already multiple times in the past. The development of the AaveDIVAWrapper contract is based on Aave V3.2. Due to AaveDIVAWrapper's reliance on only core functions (supply(), withdraw()) and the getReserveData() getter function, breaking changes to their interfaces are deemed unlikely. You can view the changelogs for new version releases here.

Main user flows

The following sections provide an overview of the main user flows of the AaveDIVAWrapper contract in more technical detail.

Pool creation / liquidity addition

The pool creation / liquidity addition flow is illustrated below:

AaveDIVAWrapper1

  1. User (e.g. donor) deposits 100 USDC (the collateral token) via AaveDIVAWrapper's createContingentPool or addLiquidity function.
  2. AaveDIVAWrapper supplies the 100 USDC to Aave (2a) and receives 100 rebasing aUSDC tokens (the aToken) (2b).
  3. AaveDIVAWrapper mints 100 wUSDC, a wrapped version of the collateral token, and uses it as proxy collateral when creating / adding liquidity to the DIVA Protocol pool.
  4. DIVA Protocol sends 100 long and 100 short tokens to the specified recipients (e.g., donor and farmer in the case of DIVA Donate).

Position token redemption

The position token redemption flow is illustrated below. Note that for clarity, the diagram shows a simplified sequence - the actual implementation handles these steps in a slightly different order for security reasons.

AaveDIVAWrapper Redemption Short drawio

  1. User (e.g. farmer) submits 100 short tokens for redemption to AaveDIVAWrapper via redeemPositionToken.
  2. AaveDIVAWrapper redeems 100 aUSDC from Aave (2a) and receives 100 USDC (2b). aUSDC tokens are burnt during this process.
  3. AaveDIVAWrapper redeems the short tokens (3a) and receives 100 wUSDC from DIVA Protocol (3b). Short tokens are burnt during this process.
  4. AaveDIVAWrapper burns 100 wTokens and transfers 100 USDC to the user.

The same redemption process applies when redeeming long tokens or when redeeming both long and short tokens simultaneously via the removeLiquidity function.

Yield claim

The yield claim flow is as follows:

  1. Owner calls the claimYield function to claim accrued yield.
  2. AaveDIVAWrapper calculates the accrued yield by taking the excess of the aToken balance (e.g., 105) over the wToken supply (e.g., 100).
  3. AaveDIVAWrapper redeems 5 aUSDC from Aave for 5 USDC and transfers them to the owner.

Invariants

At any point in time, the following invariants have to hold true:

  • aToken balance of AaveDIVAWrapper >= wToken supply
  • short token supply = long token supply = wToken supply

Function overview

Function Name Description
Core functions
createContingentPool Creates a new contingent pool, adds liquidity and sends long and short tokens to specified recipients.
addLiquidity Adds collateral to an existing pool, mints new long and short position tokens and sends them to specified recipients.
removeLiquidity Removes collateral from an existing pool, burns long and short position tokens and sends the corresponding collateral amount to the specified recipient.
redeemPositionToken Redeems position tokens (short or long tokens) for the original collateral token (e.g., USDC) and sends it to the specified recipient.
redeemWToken Allows users to convert their wTokens for the underlying collateral asset.
Owner controlled functions
registerCollateralToken Registers a new collateral token that can be used in the protocol.
claimYield Allows the owner of the contract to claim the accrued yield.
Batch functions
batchCreateContingentPool Batch version of createContingentPool function.
batchAddLiquidity Batch version of addLiquidity function.
batchRemoveLiquidity Batch version of removeLiquidity function.
batchRedeemPositionToken Batch version of redeemPositionToken function.
batchRedeemWToken Batch version of redeemWToken function.
batchRegisterCollateralToken Batch version of registerCollateralToken function.
batchClaimYield Batch version of claimYield function.
Non-core functions
approveCollateralTokenForAave Function to reset the allowance of the collateral token for the Aave V3 contract to unlimited in an unlikely scenario that it runs out.
View functions
getAccruedYield Retrieves the accrued yield that is claimable by the AaveDIVAWrapper owner.
getContractDetails Returns the addresses of DIVA Protocol, Aave V3 Pool contract and owner.
getWToken Returns the wToken address for a given collateral token.
getCollateralToken Returns the collateral token address for a given wToken.

createContingentPool

Creates a new contingent pool in DIVA Protocol using the provided parameters. Long tokens are sent to PoolParams.longRecipient and short tokens to PoolParams.shortRecipient. The caller must approve AaveDIVAWrapper to transfer the collateral token before calling this function.

Returns the Id of the newly created pool.

function createContingentPool(PoolParams calldata _poolParams) external returns (bytes32);

The PoolParams struct is defined as follows:

Parameter Type Description
referenceAsset string The metric or event whose outcome will determine the payout for long and short tokens.
expiryTime uint96 Expiration time of the pool expressed as a unix timestamp in seconds (UTC).
floor uint256 Value of the reference asset at or below which the long token pays out 0 and the short token 1 (max payout), gross of fees. Input expects an integer with 18 decimals.
inflection uint256 Value of the reference asset at which the long token pays out gradient and the short token 1-gradient, gross of fees. Input expects an integer with 18 decimals.
cap uint256 Value of the reference asset at or above which the long token pays out 1 (max payout) and the short token 0, gross of fees. Input expects an integer with 18 decimals.
gradient uint256 A value between 0 and 1 which specifies the payout per long token if the outcome is equal to inflection. Input expects an integer with collateral token decimals.
collateralAmount uint256 Amount to be deposited into AaveDIVAWrapper, which is wrapped 1:1 into the wToken and deposited as collateral into the pool. Input expects an integer with collateral token decimals.
collateralToken address Address of the ERC20 collateral token (e.g., USDT).
dataProvider address Ethereum account (EOA or smart contract) that is supposed to report the final reference asset value following pool expiration.
capacity uint256 Maximum collateral amount that a contingent pool can accept. Choose a large number (e.g., 2**256 - 1) for unlimited size. Input expects an integer with collateral token decimals.
longRecipient address Address that shall receive the long token. Any burn address except for the zero address is a valid recipient to enable conditional burn use cases.
shortRecipient address Address that shall receive the short token. Any burn address except for the zero address is a valid recipient to enable conditional burn use cases.
permissionedERC721Token address Address of the ERC721 token that transfers are restricted to. Use zero address to render the long and short tokens permissionless.

Execution steps

  1. Verify that PoolParams.collateralToken is registered in the AaveDIVAWrapper contract and has an associated wToken.
  2. Transfer collateral token from caller to the AaveDIVAWrapper contract.
  3. Supply the collateral token to Aave V3 and receive aTokens into the AaveDIVAWrapper contract.
  4. Mint an equivalent amount of wTokens to the AaveDIVAWrapper contract.
  5. Create a new contingent pool on DIVA Protocol using the provided parameters and the minted wTokens as proxy collateral. Refer to the DIVA protocol documentation for more details.
  6. Send the resulting long and short tokens to the specified recipients.
  7. Emit a PoolIssued event.
  8. Return the Id of the newly created pool.

Reverts

Error Condition Contract/Library
CollateralTokenNotRegistered When PoolParams.collateralToken is not registered in AaveDIVAWrapper AaveDIVAWrapper
ERC20InsufficientAllowance (wording depending on collateral token implementation) When caller has insufficient allowance. ERC20 (collateralToken)
ERC20InsufficientBalance (wording depending on collateral token implementation) When caller has insufficient balance. ERC20 (collateralToken)
Error code 26 When PoolParams.collateralAmount is zero. Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 28 When Aave V3 reserve is frozen. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
Error code 51 When reserve's supply cap is exceeded. Aave V3
InvalidInputParamsCreateContingentPool When pool parameters are invalid (see details below). DIVA Protocol
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin).

Note: Additional ERC20-specific revert conditions may apply (e.g., token may revert paused or when the to or from address is blacklisted).

Invalid pool parameter conditions

  • expiryTime is smaller than or equal to block.timestamp
  • referenceAsset is an empty string.
  • floor is greater than inflection.
  • cap is smaller than inflection.
  • cap is greater than 1e59.
  • dataProvider is equal to the zero address.
  • gradient is greater than 1 base unit in collateral token terms.
  • collateralAmount exceeds capacity.
  • collateralToken has more than 18 or less than 6 decimals.
  • Either longRecipient or shortRecipient are equal to the zero address. Reverts with ERC20: mint to the zero address inside the long/short ERC20 token contract.

Emitted events

Event Description Contract/Library
PoolIssued Emitted when a new pool is created. AaveDIVAWrapper
Transfer Emitted during token transfers (safeTransferFrom, mint functions). ERC20 (collateralToken, wToken)
Approval Emitted during token approvals (depends on token implementation; USDT on Ethereum does not emit an Approval event, for instance). ERC20 (collateralToken)
Supply among others (Example) Emitted when supplying to Aave V3. Aave V3
PoolIssued among others (Example) Emitted during pool creation on DIVA Protocol. DIVA Protocol

addLiquidity

Adds _collateralAmount of liquidity to an existing DIVA Protocol pool identified by the provided _poolId. Long tokens are sent to _longRecipient and short tokens to _shortRecipient. The caller must approve AaveDIVAWrapper to transfer _collateralAmount of the collateral token before calling this function.

function addLiquidity(
    bytes32 _poolId, // The Id of the DIVA Protocol pool to add liquidity to
    uint256 _collateralAmount, // The amount of collateral token to add as liquidity
    address _longRecipient, // The recipient of the long tokens
    address _shortRecipient // The recipient of the short tokens
) external;

Execution steps

  1. Verify that the collateral token used in the DIVA Protocol pool associated with _poolId corresponds to a registered collateral token in the AaveDIVAWrapper contract.
  2. Transfer the collateral token from caller to the AaveDIVAWrapper contract.
  3. Supply the collateral token to Aave V3 and receive aTokens.
  4. Mint an equivalent amount of wTokens to the AaveDIVAWrapper contract.
  5. Add _collateralAmount of liquidity to the DIVA Protocol pool associated with the provided _poolId using the minted wTokens.
  6. Send the resulting long and short tokens to the specified recipients.

Reverts

Error Condition Contract/Library
CollateralTokenNotRegistered When the pool's collateral token is not associated with a registered collateral token in AaveDIVAWrapper. AaveDIVAWrapper
ERC20InsufficientAllowance (wording depending on collateral token implementation) When caller has insufficient allowance. ERC20 (collateralToken)
ERC20InsufficientBalance (wording depending on collateral token implementation) When caller has insufficient balance. ERC20 (collateralToken)
Error code 26 When _collateralAmount is zero. Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 28 When Aave V3 reserve is frozen. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
Error code 51 When reserve's supply cap is exceeded. Aave V3
PoolExpired When the pool is already expired. DIVA Protocol
PoolCapacityExceeded When the pool capacity is exceeded. DIVA Protocol
ERC20: mint to the zero address When either _longRecipient or _shortRecipient are equal to the zero address. ERC20 (long/short tokens)
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin).

Note: Additional ERC20-specific revert conditions may apply (e.g., when the token is paused or the to or from address is blacklisted).

Emitted events

Event Description Contract/Library
Transfer Emitted during token transfers (safeTransferFrom, mint functions). ERC20 (collateralToken, wToken)
Approval Emitted during token approvals (depends on token implementation; USDT on Ethereum does not emit an Approval event, for instance). ERC20 (collateralToken)
Supply among others (Example) Emitted when supplying to Aave V3. Aave V3
LiquidityAdded among others (Example) Emitted during liquidity addition to DIVA Protocol. DIVA Protocol

removeLiquidity

Removes liquidity from the pool associated with _poolId by burning an equal amount (_positionTokenAmount) of valid long and short tokens, and then transferring the corresponding collateral tokens (e.g., USDC) to the specified _recipient. The caller must approve AaveDIVAWrapper to transfer both long and short tokens before calling this function.

Returns the amount of collateral tokens transferred to _recipient, net of DIVA fees.

function removeLiquidity(
    bytes32 _poolId, //  The pool Id to remove liquidity from
    uint256 _positionTokenAmount, // Amount of liquidity to remove (type(uint256).max = min short/long balance)
    address _recipient // The address of the recipient to receive the collateral tokens
) external returns (uint256);

Execution steps

  1. Verify that the collateral token used in the DIVA Protocol pool associated with _poolId corresponds to a registered collateral token in the AaveDIVAWrapper contract.
  2. Override _positionTokenAmount with the user's minimum short/long token balance if _positionTokenAmount equals type(uint256).max.
  3. Transfer the specified _positionTokenAmount of both short and long tokens from the caller to the AaveDIVAWrapper contract.
  4. Remove liquidity from DIVA Protocol using the transferred short and long tokens, and get the wToken amount received.
  5. Verify that _recipient is not the zero address.
  6. Burn the wTokens (AaveDIVAWrapper contract has the authority to do so as the owner).
  7. Withdraw the corresponding collateral tokens from Aave.
  8. Transfer the withdrawn collateral tokens to the specified _recipient.
  9. Return the amount transferred.

Reverts

Error Condition Contract/Library
CollateralTokenNotRegistered When the pool's collateral token is not associated with a registered collateral token in AaveDIVAWrapper. AaveDIVAWrapper
ERC20: insufficient allowance When caller has insufficient short or long token allowance. ERC20 (short/long token)
ERC20: transfer amount exceeds balance When caller has insufficient short or long token balance. ERC20 (short/long token)
ZeroAddress When _recipient is the zero address. AaveDIVAWrapper
ReturnCollateralPaused When the DIVA Protocol is paused. DIVA Protocol
FinalValueAlreadyConfirmed When the final reference value has already been reported and confirmed. DIVA Protocol
ZeroProtocolFee or ZeroSettlementFee When _positionTokenAmount is small and renders the DIVA fee zero (if DIVA fee pct != 0). DIVA Protocol
Error code 26 When collateral amount to withdraw is zero. Aave V3
Error code 93 When _recipient is the same as the aToken address. Aave V3
Error code 32 When aToken amount to redeem exceeds AaveDIVAWrapper contract's balance (should never happen). Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin).

Note: Will not revert if the Aave reserve is frozen (see here).

Emitted events

Event Description Contract/Library
WTokenRedeemed Emitted when wTokens are redeemed for collateral tokens. AaveDIVAWrapper
Transfer Emitted during token transfers. ERC20 (short/long tokens)
Approval Emitted during token approvals. ERC20 (short/long tokens)
LiquidityRemoved among others (Example) Emitted during liquidity removal in DIVA Protocol. DIVA Protocol
Withdraw among others (Example) Emitted when withdrawing from Aave V3. Aave V3

Additional notes

  • DIVA fees: The DIVA fee charged on the removal of liquidity is denominated in wToken (the pool's collateral token) and is withheld within DIVA Protocol, where it can be claimed by the respective owner. The wToken can then be converted into the underlying asset (e.g., USDC) via AaveDIVAWrapper's redeemWToken function.

redeemPositionToken

Redeems _positionTokenAmount of _positionToken (short or long token) for the collateral token (e.g., USDC) and transfers it to the specified _recipient. The caller must approve AaveDIVAWrapper to transfer the position tokens before calling this function.

Returns the amount of collateral tokens transferred to _recipient, net of DIVA fees.

function redeemPositionToken(
    address _positionToken, // The address of the position token to redeem
    uint256 _positionTokenAmount, // Amount to redeem (type(uint256).max = caller's balance)
    address _recipient // The recipient of the returned collateral tokens
) external returns (uint256);

Execution steps

  1. Verify that the pool's collateral token is associated with a registered wToken in the AaveDIVAWrapper contract.
  2. Override _positionTokenAmount with the user's position token balance if _positionTokenAmount equals type(uint256).max.
  3. Transfer the specified amount of position tokens from the caller to the AaveDIVAWrapper contract.
  4. Redeem the position token on DIVA Protocol and get the wToken amount received.
  5. Verify that _recipient is not the zero address.
  6. Burn the wTokens (AaveDIVAWrapper contract has the authority to do so as the owner).
  7. Withdraw the corresponding collateral tokens from Aave.
  8. Transfer the withdrawn collateral tokens to the specified recipient.
  9. Return the amount transferred.

Reverts

Error Condition Contract/Library
CollateralTokenNotRegistered When the pool's collateral token is not associated with a registered collateral token in AaveDIVAWrapper. AaveDIVAWrapper
ERC20: insufficient allowance When caller has insufficient position token allowance. ERC20 (positionToken)
ERC20: transfer amount exceeds balance When caller has insufficient position token balance. ERC20 (positionToken)
ZeroAddress When _recipient is the zero address. AaveDIVAWrapper
ReturnCollateralPaused When the DIVA Protocol is paused. DIVA Protocol
FinalReferenceValueNotSet When no final reference value has been reported yet. DIVA Protocol
ChallengePeriodNotExpired When a final reference value has been reported, but the challenge period didn't expire yet. DIVA Protocol
ReviewPeriodNotExpired When a final reference value has been reported, but was challenged and the subsequent review period didn't expire yet. DIVA Protocol
Error code 26 When collateral amount to withdraw is zero. Aave V3
Error code 93 When _recipient is the same as the aToken address. Aave V3
Error code 32 When aToken amount to redeem exceeds AaveDIVAWrapper contract's balance (should never happen). Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin).

Note: Will not revert if the Aave reserve is frozen (see here).

Emitted events

Event Description Contract/Library
WTokenRedeemed Emitted when wTokens are redeemed for collateral tokens. AaveDIVAWrapper
Transfer Emitted during token transfers. ERC20 (positionToken)
Approval Emitted during token approvals. ERC20 (positionToken)
PositionTokenRedeemed among others (Example) Emitted during position token redemption. DIVA Protocol
Withdraw among others (Example) Emitted when withdrawing from Aave V3. Aave V3

Additional notes

  • DIVA fees: The DIVA fee charged on redemptionis denominated in wToken and is withheld within DIVA Protocol, where it can be claimed by the respective DIVA Protocol owner. The wToken can be converted via AaveDIVAWrapper's redeemWToken function into the underlying asset (e.g., USDC).

redeemWToken

Converts the provided _wTokenAmount of _wToken into the underlying collateral token and transfers it to the specified _recipient. Users that received wTokens, e.g., by redeeming their position tokens directly from DIVA Protocol or other direct interactions, can use this function to convert them into collateral tokens (e.g., USDC). No prior approval from the caller is required for this operation as the AaveDIVAWrapper contract has the authority to burn wTokens directly from the caller's balance.

Returns the amount of collateral tokens withdrawn from Aave and transferred to _recipient.

function redeemWToken(
    address _wToken, // The address of the wToken to convert
    uint256 _wTokenAmount, // Amount to convert (type(uint256).max = caller's balance)
    address _recipient // The address of the recipient to receive the collateral tokens
) external returns (uint256);

Execution steps

  1. Use the user's balance if _wTokenAmount equals type(uint256).max.
  2. Burn the specified amount of wTokens from the caller's balance.
  3. Withdraw the corresponding amount of collateral tokens from Aave.
  4. Transfer the withdrawn collateral tokens to the specified _recipient.
  5. Return the amount transferred.

Reverts

Error Condition Contract/Library
ERC20InsufficientBalance When caller does not own the specified amount of wTokens. ERC20 (wToken)
ZeroAddress When _recipient is the zero address. AaveDIVAWrapper
Error code 26 When _wTokenAmount is zero. Aave V3
Error code 32 When aToken amount to redeem exceeds AaveDIVAWrapper contract's balance (should never happen). Aave V3
Error code 93 When _recipient is the same as the aToken address. Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin).

Note: Will not revert if the Aave reserve is frozen (see here).

Emitted events

Event Description Contract/Library
WTokenRedeemed Emitted when wTokens are redeemed for collateral tokens. AaveDIVAWrapper
Transfer Emitted during wToken burn operation. ERC20 (wToken)
Withdraw among others (Example) Emitted when withdrawing from Aave V3. Aave V3

registerCollateralToken

Registers a new _collateralToken and returns the address of the corresponding wToken. Only callable by the owner.

Returns the address of the newly created wToken.

function registerCollateralToken(address _collateralToken) external returns (address);

Execution steps

  1. Verify that the provided _collateralToken is not yet registered.
  2. Confirm that the provided _collateralToken is supported by Aave V3.
  3. Deploy a new wToken contract to represent the wrapped version of the provided _collateralToken:
    • Set the symbol as "w" + _collateralToken symbol (e.g., wUSDT).
    • Set decimals to match the _collateralToken.
    • Set the AaveDIVAWrapper contract as the owner of the wToken.
  4. Map the _collateralToken address to the newly created wToken address, and vice versa for reverse lookups.
  5. Approve unlimited wToken transfers to DIVA Protocol.
  6. Approve unlimited _collateralToken transfers to Aave V3.
  7. Emit a CollateralTokenRegistered event.
  8. Return the address of the newly created wToken.

Reverts

Error Condition Contract/Library
CollateralTokenAlreadyRegistered When _collateralToken is already registered. AaveDIVAWrapper
UnsupportedCollateralToken When _collateralToken is not supported by Aave V3. AaveDIVAWrapper
OwnableUnauthorizedAccount When function is called by an address that is not the owner. Ownable2Step (OpenZeppelin).
ReentrancyGuardReentrantCall When a reentrant call to the function is detected ReentrancyGuard (OpenZeppelin).

Emitted events

Event Description Contract/Library
CollateralTokenRegistered Emitted when a new collateral token is registered. AaveDIVAWrapper
Approval Emitted when approving token transfers. ERC20 (wToken)

Additional notes

  • Token decimals: wToken, aToken and collateral token have the same number of decimals.
  • wToken ownership: The AaveDIVAWrapper contract is set as the owner of the wToken and has exclusive rights to mint and burn them.
  • Unlimited approvals: The unlimited approvals are deemed safe as the AaveDIVAWrapper is a pass-through entity that does not hold excess wTokens or collateral tokens. Should a vulnerability be discovered in DIVA Protocol or Aave, users can simply stop interacting with the AaveDIVAWrapper contract.
  • Allowance behavior: Granting an infinite allowance for wToken does not reduce the allowance on transferFrom as it uses a newer OpenZeppelin ERC20 implementation. However, this behavior may differ for collateral tokens like USDC, DAI, or WETH used in Aave. These tokens decrement the allowance with each use of transferFrom, even if an unlimited allowance is set. Consequently, though very unlikely, AaveDIVAWrapper could eventually exhaust its allowance. The approveCollateralTokenForAave function has been implemented to manually reset the allowance for Aave to unlimited.

claimYield

Transfers the yield accrued in the provided _collateralToken to the specified _recipient. Only callable by the owner of the contract. Partial yield claims are not supported.

Returns the amount of collateral token sent to _recipient.

function claimYield(
    address _collateralToken, // The address of the collateral token to claim yield for
    address _recipient // The address of the recipient to whom the accrued yield will be transferred
) external returns (uint256);

Execution steps

  1. Verify that the caller is the contract owner (enforced by onlyOwner modifier).
  2. Verify that the provided _collateralToken is registered.
  3. Verify that the provided _recipient is not the zero address.
  4. Calculate the accrued yield for the provided _collateralToken (AaveDIVAWrapper contract's aToken balance - wToken supply).
  5. Withdraw the accrued yield from Aave V3.
  6. Transfer the withdrawn collateral token amount to the specified recipient.
  7. Emit a YieldClaimed event.
  8. Return the amount claimed.

Reverts

Error Condition Contract/Library
OwnableUnauthorizedAccount When the caller is not the owner of the contract. Ownable2Step (OpenZeppelin)
CollateralTokenNotRegistered When _collateralToken is not registered. AaveDIVAWrapper
ZeroAddress When _recipient is the zero address. AaveDIVAWrapper
Error code 26 When the accrued yield is zero. Aave V3
Error code 32 When the calculated accrued yield exceeds AaveDIVAWrapper contract's aToken balance (should never happen). Aave V3
Error code 27 When Aave V3 reserve is inactive. Aave V3
Error code 29 When Aave V3 reserve is paused. Aave V3
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin)

Note: Will not revert if the reserve is frozen (see here).

Emitted events

Event Description Contract/Library
YieldClaimed Emitted when yield is claimed by the owner. AaveDIVAWrapper
Withdraw among others (Example) Emitted when withdrawing from Aave V3. Aave V3

Additional notes

  • Yield calculation: The accrued yield is calculated as the difference between the AaveDIVAWrapper contract's aToken balance and the total supply of the associated wToken. If the aToken balance is smaller than the wToken supply (e.g., due to rounding), the function will revert with error code 26 from Aave V3. In this case, the owner should wait for more yield to accrue.

approveCollateralTokenForAave

Resets the allowance of the provided _collateralToken for the Aave V3 contract to unlimited, should it ever be depleted. Can be triggered by anyone. Using OpenZeppelin's safeIncreaseAllowance to accommodate tokens like USDT on Ethereum that require the approval to be set to zero before setting it to a non-zero value.

function approveCollateralTokenForAave(address _collateralToken) external;

Execution steps

  1. Verify that the provided _collateralToken is registered in AaveDIVAWrapper contract.
  2. Retrieve the current allowance of AaveDIVAWrapper contract for the _collateralToken on Aave V3.
  3. Calculate the difference between the maximum possible allowance and the current allowance.
  4. Increase the allowance by this difference.

Reverts

Error Condition Contract/Library
CollateralTokenNotRegistered When _collateralToken is not registered. AaveDIVAWrapper
ReentrancyGuardReentrantCall When a reentrant call to the function is detected. ReentrancyGuard (OpenZeppelin)

Note: Additional token-specific revert conditions may apply depending on the token being approved.

Emitted events

Event Description Contract/Library
Approval Emitted when approving token transfers. ERC20 (collateralToken)

batchCreateContingentPool

Batch version of createContingentPool function.

function batchCreateContingentPool(
    PoolParams[] calldata _poolParams
) external returns (bytes32[] memory);

See createContingentPool for the PoolParams structure.

batchAddLiquidity

Batch version of addLiquidity function.

function batchAddLiquidity(
    AddLiquidityArgs[] calldata _addLiquidityArgs
) external;

where AddLiquidityArgs is defined as follows:

struct AddLiquidityArgs {
    bytes32 poolId;
    uint256 collateralAmount;
    address longRecipient;
    address shortRecipient;
}

batchRemoveLiquidity

Batch version of removeLiquidity function.

function batchRemoveLiquidity(
    RemoveLiquidityArgs[] calldata _removeLiquidityArgs
) external returns (uint256[] memory);

where RemoveLiquidityArgs is defined as follows:

struct RemoveLiquidityArgs {
    bytes32 poolId;
    uint256 positionTokenAmount;
    address recipient;
}

batchRedeemPositionToken

Batch version of redeemPositionToken function.

function batchRedeemPositionToken(
    RedeemPositionTokenArgs[] calldata _redeemPositionTokenArgs
) external returns (uint256[] memory);

where RedeemPositionTokenArgs is defined as follows:

struct RedeemPositionTokenArgs {
    address positionToken;
    uint256 positionTokenAmount;
    address recipient;
}

batchRedeemWToken

Batch version of redeemWToken function.

function batchRedeemWToken(
    RedeemWTokenArgs[] calldata _redeemWTokenArgs
) external returns (uint256[] memory);

where RedeemWTokenArgs is defined as follows:

struct RedeemWTokenArgs {
    address wToken;
    uint256 wTokenAmount;
    address recipient;
}

batchClaimYield

Batch version of claimYield function.

function batchClaimYield(
    ClaimYieldArgs[] calldata _claimYieldArgs
) external returns (uint256[] memory);

where ClaimYieldArgs is defined as follows:

struct ClaimYieldArgs {
    address collateralToken;
    address recipient;
}

batchApproveCollateralTokenForAave

Batch version of approveCollateralTokenForAave function.

function batchApproveCollateralTokenForAave(
    address[] calldata _collateralTokens
) external;

getAccruedYield

Returns the total yield accrued in AaveDIVAWrapper contract in the provided _collateralToken and claimable by the owner. Returns zero if _collateralToken is not registered or if the aToken balance is smaller than the wToken supply (e.g., due to rounding).

function getAccruedYield(address _collateralToken) external view returns (uint256);

getContractDetails

Returns the DIVA Protocol and Aave V3 addresses the AaveDIVAWrapper contract is linked to as well as the owner of the contract.

function getContractDetails() external view returns (address, address, address);

getWToken

Returns the address of the wToken associated with the provided _collateralToken. Returns address(0) if the provided _collateralToken is not registered.

function getWToken(address _collateralToken) external view returns (address);

getAToken

Returns the address of Aave V3's aToken associated with the provided _collateralToken. Returns address(0) if the provided _collateralToken is not supported by Aave V3.

function getAToken(address _collateralToken) external view returns (address);

getCollateralToken

Returns the address of the collateral token associated with the provided _wToken. Returns address(0) if the provided _wToken is not registered.

function getCollateralToken(address _wToken) external view returns (address);

Miscellaneous

  • This contract does not implement receive or fallback functions and, therefore, cannot accept Ether directly through plain transactions or manage calls to non-existent functions.
  • This contract does not support deposits in Ether. To support Ether deposits, an adapter contract like this one can be placed in front.

Risk disclaimer

The use of any DeFi protocol comes with certain risks. Be responsible when interacting with the AaveDIVAWrapper contract and don't put in more funds than you are willing to lose. By interacting with AaveDIVAWrapper you acknowledge the following risks:

  • Smart contract risk: Despite following best practices in Solidity coding and conducting a smart contract audit, bugs cannot be fully excluded. Only deposit amounts that you can afford to lose.
  • Integration risk: The AaveDIVAWrapper contract integrates with Aave V3 and DIVA Protocol. Vulnerabilities or issues in either protocol (e.g., bad debt in Aave) may affect the functionality of AaveDIVAWrapper.
  • Upgradeability of Aave V3: Aave V3's upgradeable nature may introduce breaking changes in future versions which could impact the functionality of the AaveDIVAWrapper contract. However, this risk is deemed low as the AaveDIVAWrapper contract only uses a limited number of Aave's core functions which are unlikely to see breaking changes (see Upgradeability section).

Resources