Мне нужна помощь в проекте. Твоя задача использовать язык Solidity, фреймворк MUD(второй версии), а для написания тестов использовать Foundry. Мне надо написать тесты для библиотеки LibExperience.sol . Эта библиотека связана с компонентом ExperienceComponent.sol . Так же в новой версии фреймворка MUD появились таблицы, который заменили компоненты. 

Код таблицы : 

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/* Autogenerated file. Do not edit manually. */

// Import schema type
import { SchemaType } from "@latticexyz/schema-type/src/solidity/SchemaType.sol";

// Import store internals
import { IStore } from "@latticexyz/store/src/IStore.sol";
import { StoreSwitch } from "@latticexyz/store/src/StoreSwitch.sol";
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";
import { Bytes } from "@latticexyz/store/src/Bytes.sol";
import { Memory } from "@latticexyz/store/src/Memory.sol";
import { SliceLib } from "@latticexyz/store/src/Slice.sol";
import { EncodeArray } from "@latticexyz/store/src/tightcoder/EncodeArray.sol";
import { Schema, SchemaLib } from "@latticexyz/store/src/Schema.sol";
import { PackedCounter, PackedCounterLib } from "@latticexyz/store/src/PackedCounter.sol";

bytes32 constant _tableId = bytes32(abi.encodePacked(bytes16(""), bytes16("Experience")));
bytes32 constant ExperienceTableId = _tableId;

library Experience {
  /** Get the table's schema */
  function getSchema() internal pure returns (Schema) {
    SchemaType[] memory _schema = new SchemaType[](1);
    _schema[0] = SchemaType.UINT32_ARRAY;

    return SchemaLib.encode(_schema);
  }

  function getKeySchema() internal pure returns (Schema) {
    SchemaType[] memory _schema = new SchemaType[](1);
    _schema[0] = SchemaType.UINT256;

    return SchemaLib.encode(_schema);
  }

  /** Get the table's metadata */
  function getMetadata() internal pure returns (string memory, string[] memory) {
    string[] memory _fieldNames = new string[](1);
    _fieldNames[0] = "value";
    return ("Experience", _fieldNames);
  }

  /** Register the table's schema */
  function registerSchema() internal {
    StoreSwitch.registerSchema(_tableId, getSchema(), getKeySchema());
  }

  /** Register the table's schema (using the specified store) */
  function registerSchema(IStore _store) internal {
    _store.registerSchema(_tableId, getSchema(), getKeySchema());
  }

  /** Set the table's metadata */
  function setMetadata() internal {
    (string memory _tableName, string[] memory _fieldNames) = getMetadata();
    StoreSwitch.setMetadata(_tableId, _tableName, _fieldNames);
  }

  /** Set the table's metadata (using the specified store) */
  function setMetadata(IStore _store) internal {
    (string memory _tableName, string[] memory _fieldNames) = getMetadata();
    _store.setMetadata(_tableId, _tableName, _fieldNames);
  }

  /** Get value */
  function get(uint256 entity) internal view returns (uint32[3] memory value) {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    bytes memory _blob = StoreSwitch.getField(_tableId, _primaryKeys, 0);
    return toStaticArray_uint32_3(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32());
  }

  /** Get value (using the specified store) */
  function get(IStore _store, uint256 entity) internal view returns (uint32[3] memory value) {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    bytes memory _blob = _store.getField(_tableId, _primaryKeys, 0);
    return toStaticArray_uint32_3(SliceLib.getSubslice(_blob, 0, _blob.length).decodeArray_uint32());
  }

  /** Set value */
  function set(uint256 entity, uint32[3] memory value) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    StoreSwitch.setField(_tableId, _primaryKeys, 0, EncodeArray.encode(fromStaticArray_uint32_3(value)));
  }

  /** Set value (using the specified store) */
  function set(IStore _store, uint256 entity, uint32[3] memory value) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    _store.setField(_tableId, _primaryKeys, 0, EncodeArray.encode(fromStaticArray_uint32_3(value)));
  }

  /** Push an element to value */
  function push(uint256 entity, uint32 _element) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    StoreSwitch.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element)));
  }

  /** Push an element to value (using the specified store) */
  function push(IStore _store, uint256 entity, uint32 _element) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    _store.pushToField(_tableId, _primaryKeys, 0, abi.encodePacked((_element)));
  }

  /** Update an element of value at `_index` */
  function update(uint256 entity, uint256 _index, uint32 _element) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    StoreSwitch.updateInField(_tableId, _primaryKeys, 0, _index * 4, abi.encodePacked((_element)));
  }

  /** Update an element of value (using the specified store) at `_index` */
  function update(IStore _store, uint256 entity, uint256 _index, uint32 _element) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    _store.updateInField(_tableId, _primaryKeys, 0, _index * 4, abi.encodePacked((_element)));
  }

  /** Tightly pack full data using this table's schema */
  function encode(uint32[3] memory value) internal view returns (bytes memory) {
    uint16[] memory _counters = new uint16[](1);
    _counters[0] = uint16(value.length * 4);
    PackedCounter _encodedLengths = PackedCounterLib.pack(_counters);

    return abi.encodePacked(_encodedLengths.unwrap(), EncodeArray.encode(fromStaticArray_uint32_3(value)));
  }

  /* Delete all data for given keys */
  function deleteRecord(uint256 entity) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    StoreSwitch.deleteRecord(_tableId, _primaryKeys);
  }

  /* Delete all data for given keys (using the specified store) */
  function deleteRecord(IStore _store, uint256 entity) internal {
    bytes32[] memory _primaryKeys = new bytes32[](1);
    _primaryKeys[0] = bytes32(uint256((entity)));

    _store.deleteRecord(_tableId, _primaryKeys);
  }
}

function toStaticArray_uint32_3(uint32[] memory _value) pure returns (uint32[3] memory _result) {
  // in memory static arrays are just dynamic arrays without the length byte
  assembly {
    _result := add(_value, 0x20)
  }
}

function fromStaticArray_uint32_3(uint32[3] memory _value) view returns (uint32[] memory _result) {
  _result = new uint32[](3);
  uint256 fromPointer;
  uint256 toPointer;
  assembly {
    fromPointer := _value
    toPointer := add(_result, 0x20)
  }
  Memory.copy(fromPointer, toPointer, 96);
}


  Код LibExperience.sol :  
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { IUint256Component } from "solecs/interfaces/IUint256Component.sol";
import { PStat, PS_L, ExperienceComponent, ID as ExperienceComponentID } from "./ExperienceComponent.sol";
import { Experience } from "../codegen/Tables.sol";

library LibExperience {
  error LibExperience__InvalidLevel();
  error LibExperience__ExpNotInitialized();

  uint32 constant MAX_LEVEL = 16;

  function getPStats(uint256 targetEntity) internal view returns (uint32[PS_L] memory result) {
    result = Experience.get(targetEntity);
    for (uint256 i; i < result.length; i++) {
      result[i] = _getLevel(result[i]);
    }
  }

  function getPStat(uint256 targetEntity, PStat pstatIndex) internal view returns (uint32) {
    uint32 exp = Experience.get(targetEntity)[uint256(pstatIndex)];
    return _getLevel(exp);
  }

  function hasExp(uint256 targetEntity) internal view returns (bool) {
    uint32 exp = Experience.get(targetEntity);
    if (exp) {
      return true;
    } else {
      return false;
    }
  }

  function getExp(uint256 targetEntity) internal view returns (uint32[PS_L] memory) {
    return Experience.get(targetEntity);
  }

  /**
   * @dev Allow target to receive exp, set exp to 0s
   */
  function initExp(uint256 targetEntity) internal {
    uint32[PS_L] memory exp;
    Experience.set(targetEntity, exp);
  }

  /**
   * @dev Increase target's experience
   * Exp must be initialized
   */
  function increaseExp(uint256 targetEntity, uint32[PS_L] memory addExp) internal {
    // get current exp, or revert if it doesn't exist
    if (!Experience.get(targetEntity)) {
      revert LibExperience__ExpNotInitialized();
    }
    uint32[PS_L] memory exp = Experience.get(targetEntity);

    // increase
    for (uint256 i; i < PS_L; i++) {
      exp[i] += addExp[i];
    }
    // set increased exp
    Experience.set(targetEntity, exp);
  }

  /**
   * @dev Calculate aggregate level based on weighted sum of pstat exp
   */
  function getAggregateLevel(uint256 targetEntity, uint32[PS_L] memory levelMul) internal view returns (uint32) {
    uint32[PS_L] memory exp = getExp(targetEntity);
    uint256 expTotal;
    uint256 mulTotal;
    for (uint256 i; i < PS_L; i++) {
      expTotal += exp[i] * levelMul[i];
      mulTotal += levelMul[i];
    }

    expTotal /= mulTotal;

    return _getLevel(expTotal);
  }

  /**
   * @dev Calculate level based on single exp value
   */
  function _getLevel(uint256 expVal) private pure returns (uint32) {
    // expVal per level rises exponentially with polynomial easing
    // 1-0, 2-96, 3-312, 4-544, 5-804, 6-1121...
    for (uint32 level = 1; level < MAX_LEVEL; level++) {
      // (1<<i) == 2**i ; can't overflow due to maxLevel
      if (expVal < _getExpForLevel(level + 1)) {
        return level;
      }
    }
    return MAX_LEVEL;
  }

  /**
   * @dev Utility function to reverse a level into its required exp
   */
  function _getExpForLevel(uint32 level) private pure returns (uint32) {
    if (level < 1 || level > MAX_LEVEL) revert LibExperience__InvalidLevel();

    // this formula starts from 0, so adjust the arg
    if (level == 1) {
      return 0;
    } else {
      level -= 1;
    }

    return uint32(8 * (1 << level) - level ** 6 / 1024 + level * 200 - 120);
  }

  /**
   * @dev Get exp amount to get base primary to `pstats` (assuming 0 current exp).
   * This is a utility for testing
   */
  function getExpForPStats(uint32[PS_L] memory pstats) internal pure returns (uint32[PS_L] memory exp) {
    for (uint256 i; i < PS_L; i++) {
      exp[i] = _getExpForLevel(pstats[i]);
    }
  }

  /**
   * @dev same as getExpForPStats but for 1 specific pstat
   */
  function getExpForPStat(uint32 pstat) internal pure returns (uint32) {
    return _getExpForLevel(pstat);
  }
}

Код ExperienceComponent.sol :  // SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { LibTypes } from "solecs/LibTypes.sol";
import { BareComponent } from "solecs/BareComponent.sol";

enum PStat {
  STRENGTH,
  ARCANA,
  DEXTERITY
}
uint256 constant PS_L = 3;

uint256 constant ID = uint256(keccak256("component.Experience"));

contract ExperienceComponent is BareComponent {
  constructor(address _world) BareComponent(_world, ID) {}

  function getSchema() public pure override returns (string[] memory keys, LibTypes.SchemaValue[] memory values) {
    keys = new string[](PS_L);
    values = new LibTypes.SchemaValue[](PS_L);

    keys[0] = "strength";
    values[0] = LibTypes.SchemaValue.UINT32;

    keys[1] = "arcana";
    values[1] = LibTypes.SchemaValue.UINT32;

    keys[2] = "dexterity";
    values[2] = LibTypes.SchemaValue.UINT32;
  }

  function getValue(uint256 entity) public view returns (uint32[PS_L] memory result) {
    return abi.decode(getRawValue(entity), (uint32[3])); /* 3 == PS_L */
  }

  function set(uint256 entity, uint32[PS_L] memory pstats) public {
    set(entity, abi.encode(pstats));
  }
}

Так же я в своем проекте создал базу для тестов на Foundry, она называется BaseTest.sol , используй ее для написания контракта :
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { IUint256Component } from "solecs/interfaces/IUint256Component.sol";
import { World } from "solecs/World.sol";
import { getAddressById } from "solecs/utils.sol";

import { PRBTest } from "@prb/test/src/PRBTest.sol";

import { LibDeploy } from "../test/LibDeploy.sol";

// Components

import { ExperienceComponent, ID as ExperienceComponentID } from "./charstat/ExperienceComponent.sol";
import { LifeCurrentComponent, ID as LifeCurrentComponentID } from "./charstat/LifeCurrentComponent.sol";
import { ManaCurrentComponent, ID as ManaCurrentComponentID } from "./charstat/ManaCurrentComponent.sol";


abstract contract BaseTest is PRBTest {
  World world;

  address internal alice;
  address internal bob;

  IUint256Component components;
  IUint256Component systems;

  // Components
  ExperienceComponent internal experienceComponent;
  LifeCurrentComponent internal lifeCurrentComponent;
  ManaCurrentComponent internal manaCurrentComponent;
 

  function setUp() public virtual {
    alice = address(bytes20(keccak256("alice")));
    bob = address(bytes20(keccak256("bob")));

    // deploy world
    world = new World();
    world.init();

    LibDeploy.deploy(address(this), address(world), false);

    // Assign all systems and components to storage vars for convenience
    components = world.components();
    systems = world.systems();
    // Components

    experienceComponent = ExperienceComponent(getAddressById(components, ExperienceComponentID));
    lifeCurrentComponent = LifeCurrentComponent(getAddressById(components, LifeCurrentComponentID));
    manaCurrentComponent = ManaCurrentComponent(getAddressById(components, ManaCurrentComponentID));
  
  }
}                I need help with a project. Your task is to use the Solidity language, the MUD framework, and use Foundry to write tests. I need to write tests for the LibExperience.sol library. This library is linked to the ExperienceComponent.sol component.

LibExperience.sol code:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { IUint256Component } from "solecs/interfaces/IUint256Component.sol";
import { getAddressById } from "solecs/utils.sol";
import { PStat, PS_L, ExperienceComponent, ID as ExperienceComponentID } from "./ExperienceComponent.sol";

library LibExperience {
  error LibExperience__InvalidLevel();
  error LibExperience__ExpNotInitialized();

  uint32 constant MAX_LEVEL = 16;

  struct Self {
    ExperienceComponent comp;
    uint256 targetEntity;
  }

  function __construct(IUint256Component components, uint256 targetEntity) internal view returns (Self memory) {
    return
      Self({
        comp: ExperienceComponent(getAddressById(components, ExperienceComponentID)),
        targetEntity: targetEntity
      });
  }

  function getPStats(Self memory __self) internal view returns (uint32[PS_L] memory result) {
    result = __self.comp.getValue(__self.targetEntity);
    for (uint256 i; i < result.length; i++) {
      result[i] = _getLevel(result[i]);
    }
  }

  function getPStat(Self memory __self, PStat pstatIndex) internal view returns (uint32) {
    uint32 exp = __self.comp.getValue(__self.targetEntity)[uint256(pstatIndex)];
    return _getLevel(exp);
  }

  function hasExp(Self memory __self) internal view returns (bool) {
    return __self.comp.has(__self.targetEntity);
  }

  function getExp(Self memory __self) internal view returns (uint32[PS_L] memory) {
    return __self.comp.getValue(__self.targetEntity);
  }

  /**
   * @dev Allow target to receive exp, set exp to 0s
   */
  function initExp(Self memory __self) internal {
    uint32[PS_L] memory exp;
    __self.comp.set(__self.targetEntity, exp);
  }

  /**
   * @dev Increase target's experience
   * Exp must be initialized
   */
  function increaseExp(Self memory __self, uint32[PS_L] memory addExp) internal {
    // get current exp, or revert if it doesn't exist
    if (!__self.comp.has(__self.targetEntity)) {
      revert LibExperience__ExpNotInitialized();
    }
    uint32[PS_L] memory exp = __self.comp.getValue(__self.targetEntity);

    // increase
    for (uint256 i; i < PS_L; i++) {
      exp[i] += addExp[i];
    }
    // set increased exp
    __self.comp.set(__self.targetEntity, exp);
  }

  /**
   * @dev Calculate aggregate level based on weighted sum of pstat exp
   */
  function getAggregateLevel(Self memory __self, uint32[PS_L] memory levelMul) internal view returns (uint32) {
    uint32[PS_L] memory exp = getExp(__self);
    uint256 expTotal;
    uint256 mulTotal;
    for (uint256 i; i < PS_L; i++) {
      expTotal += exp[i] * levelMul[i];
      mulTotal += levelMul[i];
    }

    expTotal /= mulTotal;

    return _getLevel(expTotal);
  }

  /**
   * @dev Calculate level based on single exp value
   */
  function _getLevel(uint256 expVal) private pure returns (uint32) {
    // expVal per level rises exponentially with polynomial easing
    // 1-0, 2-96, 3-312, 4-544, 5-804, 6-1121...
    for (uint32 level = 1; level < MAX_LEVEL; level++) {
      // (1<<i) == 2**i ; can't overflow due to maxLevel
      if (expVal < _getExpForLevel(level + 1)) {
        return level;
      }
    }
    return MAX_LEVEL;
  }

  /**
   * @dev Utility function to reverse a level into its required exp
   */
  function _getExpForLevel(uint32 level) private pure returns (uint32) {
    if (level < 1 || level > MAX_LEVEL) revert LibExperience__InvalidLevel();

    // this formula starts from 0, so adjust the arg
    if (level == 1) {
      return 0;
    } else {
      level -= 1;
    }

    return uint32(8 * (1 << level) - level ** 6 / 1024 + level * 200 - 120);
  }

  /**
   * @dev Get exp amount to get base primary to `pstats` (assuming 0 current exp).
   * This is a utility for testing
   */
  function getExpForPStats(uint32[PS_L] memory pstats) internal pure returns (uint32[PS_L] memory exp) {
    for (uint256 i; i < PS_L; i++) {
      exp[i] = _getExpForLevel(pstats[i]);
    }
  }

  /**
   * @dev same as getExpForPStats but for 1 specific pstat
   */
  function getExpForPStat(uint32 pstat) internal pure returns (uint32) {
    return _getExpForLevel(pstat);
  }
}  ExperienceComponent.sol code: 

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { LibTypes } from "solecs/LibTypes.sol";
import { BareComponent } from "solecs/BareComponent.sol";

enum PStat {
  STRENGTH,
  ARCANA,
  DEXTERITY
}
uint256 constant PS_L = 3;

uint256 constant ID = uint256(keccak256("component.Experience"));

contract ExperienceComponent is BareComponent {
  constructor(address _world) BareComponent(_world, ID) {}

  function getSchema() public pure override returns (string[] memory keys, LibTypes.SchemaValue[] memory values) {
    keys = new string[](PS_L);
    values = new LibTypes.SchemaValue[](PS_L);

    keys[0] = "strength";
    values[0] = LibTypes.SchemaValue.UINT32;

    keys[1] = "arcana";
    values[1] = LibTypes.SchemaValue.UINT32;

    keys[2] = "dexterity";
    values[2] = LibTypes.SchemaValue.UINT32;
  }

  function getValue(uint256 entity) public view returns (uint32[PS_L] memory result) {
    return abi.decode(getRawValue(entity), (uint32[3])); /* 3 == PS_L */
  }

  function set(uint256 entity, uint32[PS_L] memory pstats) public {
    set(entity, abi.encode(pstats));
  }
}  I also created a base for tests on Foundry in my project, it is called BaseTest.sol , use it to write a contract:
 // SPDX-License-Identifier: MIT

pragma solidity ^0.8.17;

import { IUint256Component } from "solecs/interfaces/IUint256Component.sol";
import { World } from "solecs/World.sol";
import { getAddressById } from "solecs/utils.sol";

import { PRBTest } from "@prb/test/src/PRBTest.sol";

import { LibDeploy } from "../test/LibDeploy.sol";

// Components

import { ExperienceComponent, ID as ExperienceComponentID } from "./charstat/ExperienceComponent.sol";
import { LifeCurrentComponent, ID as LifeCurrentComponentID } from "./charstat/LifeCurrentComponent.sol";
import { ManaCurrentComponent, ID as ManaCurrentComponentID } from "./charstat/ManaCurrentComponent.sol";


abstract contract BaseTest is PRBTest {
  World world;

  address internal alice;
  address internal bob;

  IUint256Component components;
  IUint256Component systems;

  // Components
  ExperienceComponent internal experienceComponent;
  LifeCurrentComponent internal lifeCurrentComponent;
  ManaCurrentComponent internal manaCurrentComponent;
 

  function setUp() public virtual {
    alice = address(bytes20(keccak256("alice")));
    bob = address(bytes20(keccak256("bob")));

    // deploy world
    world = new World();
    world.init();

    LibDeploy.deploy(address(this), address(world), false);

    // Assign all systems and components to storage vars for convenience
    components = world.components();
    systems = world.systems();
    // Components

    experienceComponent = ExperienceComponent(getAddressById(components, ExperienceComponentID));
    lifeCurrentComponent = LifeCurrentComponent(getAddressById(components, LifeCurrentComponentID));
    manaCurrentComponent = ManaCurrentComponent(getAddressById(components, ManaCurrentComponentID));
  
  }
}