In DeFi, generally speaking, an adapter is any smart contract that integrates unrelated DeFi protocols with each other. The purpose of Ocean adapter smart contracts is to connect Shell with external protocols.
Ocean Adapters
The Ocean Adapter is compatible with Shell v3, which introduced certain changes to the Ocean smart contract.
An Ocean Adapter won't work with older versions of Shell.
The Ocean Adapter is a special version of a DeFi Adapter smart contract that is written according to the Ocean specification, i.e. it inherits the special abstract contract OceanAdapter in order to integrate external projects and protocols, and make them compatible with the Ocean.
Anyone can write an OceanAdapter, to connect Shell Protocol and any project of the creator's liking. It's completely permissionless!
You can see a few example of the Ocean Adapter in the following links in our repo:
Curve2PoolAdapter - coming soon
CurveTricryptoAdapter - coming soon
Why integrate with the Ocean?
As a fully open source and permissionless protocol, writing an Ocean Adapter is available to anyone. External projects can write their own adapters and integrate with Shell Protocol, getting the best out of its composability and unique architecture.
Integrated projects do not only become compatible with the Shell Protocol, but also with one another.
And last but not least, new protocols that are just starting and may not have big numbers in TVL and other metrics will get to tap into a much bigger user base than they would initially have. Every user on Shell Protocol becomes their user as well.
Creating an Ocean Adapter
In order to create an Ocean Adapter smart contract, developers must inherit the abstract contract OceanAdapter.sol, passing Ocean contract and Primitive contract addresses (the address for the contract the integration is written for) to its constructor like in the example below:
One requirement that has to be satisfied is implementation of the three methods that were specified, but not implemented by the inherited abstract contract OceanAdapter.sol, and those methods are: primitiveOutputAmount, wrapToken and unwrapToken.
A reference of the actual implementation (which may be different for each Adapter) can be seen in Curve2PoolAdapter example:
/*** @dev wraps the underlying token into the Ocean* @param tokenId Ocean ID of token to wrap* @param amount wrap amount*/functionwrapToken(uint256 tokenId,uint256 amount) internaloverride {address tokenAddress = underlying[tokenId]; Interaction memory interaction =Interaction({ interactionTypeAndAddress:_fetchInteractionId(tokenAddress,uint256(InteractionType.WrapErc20)), inputToken:0, outputToken:0, specifiedAmount: amount, metadata:bytes32(0) });IOceanInteractions(ocean).doInteraction(interaction);}/*** @dev unwraps the underlying token from the Ocean* @param tokenId Ocean ID of token to unwrap* @param amount unwrap amount*/functionunwrapToken(uint256 tokenId,uint256 amount) internaloverridereturns (uint256 unwrappedAmount) {address tokenAddress = underlying[tokenId]; Interaction memory interaction =Interaction({ interactionTypeAndAddress:_fetchInteractionId(tokenAddress,uint256(InteractionType.UnwrapErc20)), inputToken:0, outputToken:0, specifiedAmount: amount, metadata:bytes32(0) });IOceanInteractions(ocean).doInteraction(interaction);// handle the unwrap fee scenariouint256 unwrapFee = amount /IOceanInteractions(ocean).unwrapFeeDivisor(); (,uint256 truncated) =_convertDecimals(NORMALIZED_DECIMALS, decimals[tokenId], amount - unwrapFee); unwrapFee = unwrapFee + truncated; unwrappedAmount = amount - unwrapFee;}/*** @dev swaps/add liquidity/remove liquidity from Curve 2pool* @param inputToken The user is giving this token to the pool* @param outputToken The pool is giving this token to the user* @param inputAmount The amount of the inputToken the user is giving to the pool* @param minimumOutputAmount The minimum amount of tokens expected back after the exchange*/functionprimitiveOutputAmount(uint256 inputToken,uint256 outputToken,uint256 inputAmount,bytes32 minimumOutputAmount)internaloverridereturns (uint256 outputAmount){ (uint256 rawInputAmount,) =_convertDecimals(NORMALIZED_DECIMALS, decimals[inputToken], inputAmount); ComputeType action =_determineComputeType(inputToken, outputToken);uint256 rawOutputAmount;// avoid multiple SLOADSint128 indexOfInputAmount = indexOf[inputToken];int128 indexOfOutputAmount = indexOf[outputToken];if (action == ComputeType.Swap) { rawOutputAmount =ICurve2Pool(primitive).exchange(indexOfInputAmount, indexOfOutputAmount, rawInputAmount,0); } elseif (action == ComputeType.Deposit) {uint256[2] memory inputAmounts; inputAmounts[uint256(int256(indexOfInputAmount))] = rawInputAmount; rawOutputAmount =ICurve2Pool(primitive).add_liquidity(inputAmounts,0); } else { rawOutputAmount =ICurve2Pool(primitive).remove_liquidity_one_coin(rawInputAmount, indexOfOutputAmount,0); } (outputAmount,) =_convertDecimals(decimals[outputToken], NORMALIZED_DECIMALS, rawOutputAmount);if (uint256(minimumOutputAmount) > outputAmount) revertSLIPPAGE_LIMIT_EXCEEDED();if (action == ComputeType.Swap) {emitSwap(inputToken, inputAmount, outputAmount, minimumOutputAmount, primitive,true); } elseif (action == ComputeType.Deposit) {emitDeposit(inputToken, inputAmount, outputAmount, minimumOutputAmount, primitive,true); } else {emitWithdraw(outputToken, inputAmount, outputAmount, minimumOutputAmount, primitive,true); }}
Other than that, smart contract developers are free to add arbitrarily complex logic and custom functionality to their Adapters.
The full implementation of abstract contract OceanAdapter.sol can be found here: [link coming soon].