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
Writing Ocean Adapter was introduced in the Shell v3 which required certain changes in the Ocean contract.
Trying to write Ocean Adapters and making them work with older versions won't work.
Ocean Adapter is the special version of a DeFi Adapter smart contract that is written according to the Ocean specification, i.e. inherits the special abstract contract OceanAdapter in order to integrate external project and protocols and make them compatible with the Ocean.
Anyone can write an OceanAdapter creating the synergy and compatibility between Shell Protocol and project of creators liking. It's completely permissionless, there's no allow list or anything similar.
You can see a few example of Ocean Adapter written by the at the following links in our repo:
Curve2PoolAdapter - coming soon
CurveTricryptoAdapter - coming soon
Why integrate with the Ocean?
As a fully open system and code base, writing an Ocean Adapter is available to anyone. External projects and protocols can write their own adapters and integrate with Shell Protocol, getting the best out of its composability and unique architecture.
With each integration project and protocol do not only integrate and 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 are getting the opportunity of tapping into the much bigger user base than they would initially have. Every Shell Protocol's user immediately becomes their's as well.
Creating an Ocean Adapter
In order to create Ocean Adapter smart contract developers must inherit abstract contract OceanAdapter.sol passing Ocean contract and Primitive contract addresses (address for the contract we integration is written for) to it's 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].