Merkl Developer Portal logo

Token wrappers

Deploy a token wrapper contract, mint wrapper tokens, and use them as the reward token of a Merkl campaign

A token wrapper is an ERC-20 contract that you use as the reward token of a campaign. When a user claims, the wrapper runs custom logic and delivers the final asset. With a pull-based wrapper, the underlying tokens stay in your address until the claim.

This page covers the technical setup. For the principle and the use cases, read Keep Custody with Token Wrappers.

Templates

Merkl provides audited templates in the merkl-contracts repository:

Pull-on-Claim Wrapper

Keep custody of your tokens. They are only pulled from your address when recipients claim.
Large airdrops where you want to keep custody of tokens until they are actually claimed
Rebasing tokens: keep the rebase yield on your tokens while the campaign is running
Large campaigns with uncertain final budget: mint a large amount of wrapper tokens without needing the full underlying capital upfront

aToken Unwrapper (Aave)

Designed for Aave aTokens. Withdraws from the Aave v3 pool at claim time so claimants receive the base asset.
Distributing Aave aToken rewards while letting users receive the underlying asset
Campaigns where the creator holds aTokens and wants claimants to get the unwrapped asset

Auto-Vault Wrapper (ERC-4626)

Doesn't dilute vault yield until tokens are claimed, and enables claimants to seamlessly autocompound their rewards.
Campaigns targeting Morpho or Euler vaults: no yield dilution until tokens are actually claimed
Seamless autocompounding: claimants receive vault shares and start earning yield immediately

Native Token Unwrapper

Distributes wETH but unwraps it to native ETH when users claim.
Any campaign distributing wETH where you want claimants to receive native ETH
Improving UX for claimants who prefer native tokens over wrapped versions
WrapperContractConstructor arguments
Pull-on-ClaimPullTokenWrapperAllowImmutable_token, _distributionCreator, _holder
aToken Unwrapper (Aave)PullTokenWrapperWithdrawImmutable_token (the aToken), _distributionCreator, _holder
Auto-Vault (ERC-4626)PullTokenWrapperVaultImmutable_token, _distributionCreator, _holder, _vault
Native Token UnwrapperNativeTokenUnwrapperImmutable_wrappedNative, _distributionCreator, _holder, _name, _symbol
  • _distributionCreator is the Merkl DistributionCreator contract on the target chain. Find its address in Chains and contracts.
  • _holder is the address that holds the underlying tokens, typically your multisig.

Deploy

Deploy the wrapper from Merkl Studio with a connected wallet. You can also deploy it with your own tooling from the merkl-contracts repository.

After deployment, Studio gives a forge verify-contract command. Run it from the merkl-contracts repository to verify the wrapper on Etherscan:

forge verify-contract <WRAPPER_ADDRESS> contracts/partners/tokenWrappers/PullTokenWrapperAllowImmutable.sol:PullTokenWrapperAllowImmutable \
  --chain-id <CHAIN_ID> \
  --constructor-args $(cast abi-encode "constructor(address,address,address)" <TOKEN> <DISTRIBUTION_CREATOR> <HOLDER>) \
  --etherscan-api-key <YOUR_API_KEY>

Whitelisting is mandatory. You cannot use a wrapper in a campaign before Merkl whitelists it. Submit a whitelisting request after deployment.

Create the campaign

  1. From the holder address, call mint(address to, uint256 amount) on the wrapper. Mint the campaign budget to the address that creates the campaign.
  2. From the creator address, approve the wrapper token to the DistributionCreator contract.
  3. Create the campaign with the wrapper token as the reward token. Follow the usual flow: configuration, payloads, and transactions.

Enable claims

  1. From the holder address, approve the wrapper contract to spend the underlying token.
  2. Keep the holder balance and allowance above the expected claims.

When a user claims, the wrapper calls transferFrom on the underlying token, from the holder to the user. If the holder balance or allowance is lower than the claimed amount, the claim reverts.

To stop claims, revoke the allowance.

Read wrappers from the API

The API links each wrapper to the token it pays out with the underlyingTokenId field. See Resolving a wrapper's underlying token.