abstract contract TokenAdapter is AssetAdapterWithFees, TokenTransferrer {

    uint16 internal constant TOKEN_TYPE_ID = 2;
    uint16 internal constant TOKEN_ASSET_DATA_LENGTH = 86;
    mapping (address => uint256) internal accumulatedFees;

    constructor() AssetAdapter(TOKEN_TYPE_ID) {}

    /**
    * @dev extract the amount from the asset data bytes. Token assetData bytes contents:
    * offset length type     contents
    * +00    32     uint256  data length (== 0x36 == 54 bytes)
    * +32     2     uint16   asset type  (== TOKEN_TYPE_ID == 2)
    * +34    32     uint256  token amount in units
    * +66    32     uint256  fee amount in units
    * +98    20     address  token contract address
    */
    function getAmount(bytes memory assetData) internal override pure returns (uint256 amount) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            amount := mload(add(assetData, 34))
        }
    }

    /**
     * @dev extract the fee from the asset data bytes. See getAmount for bytes contents.
     */
    function getFee(bytes memory assetData) internal override pure returns (uint256 fee) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            fee := mload(add(assetData, 66))
        }
    }

    /**
     * @dev To retrieve the address at offset 98, get the word at offset 86 and return its last
     * 20 bytes. See `getAmount` for byte offsets table.
     */
    function getTokenAddress(bytes memory assetData) internal pure returns (address tokenAddress) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            tokenAddress := and(
                mload(add(assetData, 86)),
                0xffffffffffffffffffffffffffffffffffffffff
            )
        }
    }

    function rawSendAsset(
        bytes memory assetData,
        uint256 _amount,
        address payable _to
    ) internal override returns (bool success) {
        return doTokenTransfer(getTokenAddress(assetData), _to, _amount);
    }

    function accumulateFee(bytes memory assetData) internal override {
        accumulatedFees[getTokenAddress(assetData)] += getFee(assetData);
    }

    function withdrawFees(
        bytes calldata assetData,
        address payable _to
    ) external override onlyOwner returns (bool success) {
        address token = getTokenAddress(assetData);
        uint256 fees = accumulatedFees[token];
        accumulatedFees[token] = 0;
        return doTokenTransfer(getTokenAddress(assetData), _to, fees);
    }

    function checkAssetData(bytes memory assetData, address _pool) internal override view {
        require(assetData.length == TOKEN_ASSET_DATA_LENGTH, "adl");  // "invalid asset data length"
        require(
            RampInstantTokenPoolInterface(_pool).token() == getTokenAddress(assetData),
            "pta"  // "invalid pool token address"
        );
    }

    function getAccumulatedFees(address _assetAddress) public override view returns (uint256) {
        return accumulatedFees[_assetAddress];
    }

}
/**
 * An adapter for handling ERC-20-based token assets.
 *
 * @author Ramp Network sp. z o.o.
 */
function getTokenAddress(bytes memory assetData) internal pure returns (address tokenAddress) {
    // solhint-disable-next-line no-inline-assembly
    assembly {
        tokenAddress := and(
            mload(add(assetData, 86)),
            0xffffffffffffffffffffffffffffffffffffffff
        )
    }
}

/**
 * @dev To retrieve the address at offset 98, get the word at offset 86 and return its last
 * 20 bytes. See `getAmount` for byte offsets table.
 */