🔌Integrating with DeFi Protocols
Protect the privacy of users of any DeFi protocol by integrating Labyrinth
Introduction
The integration of Labyrinth with any DeFi protocol aims to supercharge the users of that protocol with the privacy and compliance features of Labyrinth. In this integration example, we integrate Labyrinth with Uniswap, one of the leading decentralized exchange protocol offering liquidity and trading capabilities. This integration will enable users to execute trades on Uniswap while maintaining complete privacy over their transactions.
Benefits of Integrating with Uniswap
Enhanced Privacy: Users can trade on Uniswap without exposing their wallet address.
Selective Deanonymisation: Transactions can be decoded when necessary using a revoker and a guardian network, ensuring compliance and accountability.
Seamless Experience: The integration ensures that users can leverage the best of both protocols without compromising on usability.
Getting Started
Prerequisites
Before integrating Labyrinth with an external DeFi protocol, ensure you have the following prerequisites in place:
Labyrinth Protocol as Submodule: Include the Labyrinth protocol it as a submodule in your project. This will provide the necessary functionalities required for the integration.
Interface/Periphery Contracts of the Target DeFi Protocol: Obtain the interface of the periphery contracts of the DeFi protocol you are integrating with. These contracts will be essential for interacting with the protocol’s core functionalities.
Address of the Periphery Contracts: Ensure you have the correct addresses of the periphery contracts of the DeFi protocol you're integrating with, on the deployment network (e.g., Ethereum mainnet, testnet, etc.). These addresses are crucial for interfacing with the target protocol.
Involved Token Addresses: Identify and obtain the addresses of the tokens involved in the transactions on the deployment network. This includes both the tokens you plan to trade and any intermediary tokens required by the protocol.
With these prerequisites in place, you are ready to proceed with the installation and setup of the Labyrinth protocol. Let's go!
Installation
To integrate Labyrinth with your chosen DeFi protocol, follow these steps to install and set up the necessary tools and libraries. This guide assumes you are using Foundry and Solidity for smart contract development.
Install Foundry: Foundry is a fast, portable, and modular toolkit for Ethereum application development. Installing Foundry will also handle the installation of the Solidity compiler (solc).
Install Labyrinth Protocol
Install the Labyrinth protocol using Foundry:
Create the interface of the periphery contract
Include just the interface of the periphery contract of the DeFi protocol you are integrating with, in your project directory, under the interfaces
folder.
We advise against including the complete DeFi protocol as a submodule as you can run into version conflicts with the Solidity compiler and other common dependencies like OpenZeppelin.
Install OpenZeppelin Contracts
OpenZeppelin provides a library of modular, reusable, and secure smart contracts for Ethereum. Install OpenZeppelin contracts for secure ERC20 related operations.
With these steps completed, your development environment is set up, and you are ready to proceed with integrating zkFi with your target DeFi protocol.
Transaction Flow
You will use the zkFi SDK to create and submit a ZTransaction
to zkFi Pool proxy contract for execution of your transaction. Here’s how your ZTransaction
will flow:
Labyrinth `IAdaptor` implementation
Labyrinth offers the IAdaptor
interface for protocol devs to implement in their DeFi adaptors. This interface offers a adaptorConnect(...)
function:
inAssetIds
: The array of incoming asset ids involved in the transaction.
Labyrinth offers a struct Asset
to whitelist the assets you’re going to use, during Labyrinth protocol initialization itself. The asset IDs used as inAssetIds
and outAssetIds
are returned once an asset is whitelisted during Labyrinth initialization. We will cover this in the deployment section.
inValues
: The quantity of each incoming asset.
payload
: Miscellaneous parameters (encoded as bytes) that your DeFi adaptor might need for the transaction to the target DeFi protocol. These are generally parameters expected by the target protocol to execute. For eg: Uniswap expects information of outToken
, beneficiary
, slippage protection
, etc to execute a swap, which can all be encoded in the payload
params and made available to your adaptor contract by decoding it.
Here’s an implementation of the adaptorConnect(...)
function of the UniswapV3 adaptor which executes a swap between WETH<>USDC tokens:
The adaptorConnect(...)
is called via delegatecall
by the zkFi::AdaptorHandler.sol
, with all the required input tokens/assets already in balance. The converted assets or outputs are then returned in the same function.
We highly recommend making the external calls through interfaces, rather than low-level calls like contractAddress.call(...)
to reduce chances of reverts caused due to parameter encoding.
Testing Adaptor contract guide:
In order to test your adaptor, we provide you with a ready to use test setup and scripts to abstract complexity and offer a smooth dev experience.
Main Imports:
BaseTest.t.sol
: Test setup which offersBaseTest::_loadZTx()
function to load the ZTransaction to execute the swap. Before you can use this, you will have to create these ZTransaction using the js-ffi provided by zkFi. We will cover this in detail in the test setup section.ZkFiDeploy.s.sol
: Deploy script for deploying the complete zkFi protocol. It will return the instance of the pool proxy on which you will call thePool::transact()
function to execute your ZTransaction created in the previous step. Refer the transaction flow above for more clarity.zkFi Pool
: Offers thesetConvertProxy(address adaptor, bool enable)
function which whitelist your adaptor with the zkFi protocol. If not whitelisted, the zkFi protocol will throw aUnsupportedAdaptor()
error and will not delegateCall your adaptor.Adaptor: We will deploy our DeFi adaptor in the test setup to be used in our tests.
Creating zTransaction using js-ffi
zkFi has included some TS scripts as part of the protocol using the foreign function interface offered by Solidity. These scripts can be used by you to generate the zTransactions
for submitting to the Pool::transact()
.
To view the pre-existing scripts, open the genFixtures.ts
file at lib/v1-protocol/test/js-ffi/genFixtures.ts
.
The following object define the properties required to generate the zTransaction:
In order to modify the transaction to suit your testing requirements, you can just modify the values of these properties. For eg, suppose you want to deposit a 100 WETH instead of 1, here’s how you can modify/add the transaction object.
Do note the assetIds property. These are deterministic following the pattern: BytesPrefix - AssetTypeId (1 byte) - UID (2 bytes)
Eg: const wethAssetId = 0x010001 0x = bytes prefix 01 = ID for ERC20 tokens 0001 = UID 1 alloted to the first token added
These can also be derived by converting the asset id returned by the zkFi Asset::addAsset() into it’s hexadecimal representation. 65537 ⇒ 0x010001
Once the request objects are defined, include them in the reqs object of the genFixtures::main() function and run the script:
You will see the ZTransactions outputs as .txt files in the test/fixtures/ztx
folder . Now you will be able to call these zTransactions inside your test suite using the BaseTest::_loadZTx()
function.
Writing your tests
Now with the imports and zTransactions in place, you are all set to test your DeFi adaptor!
Lets setup our test suite:
Make sure to provide zkFi pool access to the input tokens before executing any transaction like transfer or convert through the zkFi protocol. Therefore we have included the deposit_1_weth
zTx in our test setup() itself.
Test if the adaptor got deployed
Test the core feature of your adaptor
If you’re curious to know how a ZTransaction submitted to the Pool::transact() flows through the zkFi protocol > adaptor > target DeFi protocol and back, refer the Transaction Flow section.
Run your tests
Fork the mainnet/testnet on Anvil, in a new terminal tab:
We higly recommend testing against mainnet forks as most DeFi protocols are already deployed on mainnets. Also you will not run into liquidity deficit issues.
Deploy
Once you’re done testing your DeFi adaptor, it’s finally time to ship!
If you’re deploying on a network which has the zkFi and the target DeFi protocols, then you’re in luck. Just grab the zkFi PoolProxy
contract address, the periphery contract
addresses of your target protocol and just deploy your adaptor with the required addresses:
If either protocols are not deployed on your target network, please refer their respective documentation, to deploy the protocols on your target network, before proceeding with your adaptor deployment.
Last updated