contract TokenAdapter is AssetAdapterWithFees {

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

    constructor() internal AssetAdapter(TOKEN_TYPE_ID) {}

    /**
    * @dev 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    20     address  token contract address
    */
    function getAmount(bytes memory assetData) internal pure returns (uint256 amount) {
        // solium-disable-next-line security/no-inline-assembly
        assembly {
            amount := mload(add(assetData, 34))
        }
    }

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

    function rawSendAsset(
        bytes memory assetData,
        uint256 _amount,
        address payable _to
    ) internal returns (bool success) {
        Erc20Token token = Erc20Token(getTokenAddress(assetData));
        return token.transfer(_to, _amount);
    }

    function rawAccumulateFee(bytes memory assetData, uint256 _amount) internal {
        accumulatedFees[getTokenAddress(assetData)] += _amount;
    }

    function withdrawFees(
        bytes calldata assetData,
        address payable _to
    ) external onlyOwner returns (bool success) {
        address token = getTokenAddress(assetData);
        uint256 fees = accumulatedFees[token];
        accumulatedFees[token] = 0;
        require(Erc20Token(token).transfer(_to, fees), "fees transfer failed");
        return true;
    }

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

}
function getTokenAddress(bytes memory assetData) internal pure returns (address tokenAddress) {
    // solium-disable-next-line security/no-inline-assembly
    assembly {
        tokenAddress := and(
            mload(add(assetData, 54)),
            0xffffffffffffffffffffffffffffffffffffffff
        )
    }
}

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