# Using the Chainlink Local simulator in Remix IDE
Source: https://docs.chain.link/chainlink-local/build/ccip/remix/local-simulator

> For the complete documentation index, see [llms.txt](/llms.txt).

<Common callout="importPackage" />

In this guide, you will test the [Chainlink CCIP getting started guide](/ccip/evm/getting-started) locally in Remix IDE. You will:

- Deploy a CCIP sender contract
- Deploy a CCIP receiver contract
- Use the local simulator to send data from the sender contract to the receiver contract

## Prerequisites

[Remix IDE](https://remix.ethereum.org/) is an online development environment that allows you to write, deploy, and test smart contracts. By default Remix IDE
does not persist the files that you open from an external source. To save files, you will need to manually create a workspace and copy the files into the workspace.

1. Open the [Remix IDE](https://remix.ethereum.org/) in your browser.

2. Create a [new workspace](https://remix-ide.readthedocs.io/en/latest/file_explorer.html#new-workspace).

3. Copy the content of the [Test Chainlink Local Simulator contract](https://docs.chain.link/samples/CCIP/TestCCIPLocalSimulator.sol) into a new file in the workspace.

   ```sol
   // SPDX-License-Identifier: MIT
   pragma solidity 0.8.24;

   // solhint-disable no-unused-import
   // import {CCIPLocalSimulator} from "@chainlink/local/src/ccip/CCIPLocalSimulator.sol";
   ```

4. Copy the content of the [Sender contract](https://docs.chain.link/samples/CCIP/Sender.sol) into a new file in the workspace.

   ```sol
   // SPDX-License-Identifier: MIT
   pragma solidity 0.8.24;

   import {IRouterClient} from "@chainlink/contracts-ccip/contracts/interfaces/IRouterClient.sol";
   import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
   import {ExtraArgsCodec} from "@chainlink/contracts-ccip/contracts/libraries/ExtraArgsCodec.sol";
   import {OwnerIsCreator} from "@chainlink/contracts/src/v0.8/shared/access/OwnerIsCreator.sol";
   import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol";

   /**
    * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
    * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
    * DO NOT USE THIS CODE IN PRODUCTION.
    */

   /// @title - A simple contract for sending string data across chains.
   contract Sender is OwnerIsCreator {
     error NotEnoughBalance(uint256 currentBalance, uint256 calculatedFees);

     event MessageSent(
       bytes32 indexed messageId,
       uint64 indexed destinationChainSelector,
       address receiver,
       string text,
       address feeToken,
       uint256 fees
     );

     IRouterClient private s_router;
     LinkTokenInterface private s_linkToken;

     /// @notice Constructor initializes the contract with the router address.
     /// @param _router The address of the router contract.
     /// @param _link The address of the LINK token contract.
     constructor(
       address _router,
       address _link
     ) {
       s_router = IRouterClient(_router);
       s_linkToken = LinkTokenInterface(_link);
     }

     /// @notice Sends data to receiver on the destination chain.
     /// @dev Assumes your contract has sufficient LINK to cover fees.
     /// @param destinationChainSelector The identifier (aka selector) for the destination blockchain.
     /// @param receiver The address of the recipient on the destination blockchain.
     /// @param text The string text to be sent.
     /// @return messageId The ID of the message that was sent.
     function sendMessage(
       uint64 destinationChainSelector,
       address receiver,
       string calldata text
     ) external onlyOwner returns (bytes32 messageId) {
       // Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
       Client.EVM2AnyMessage memory evm2AnyMessage = Client.EVM2AnyMessage({
         receiver: abi.encode(receiver), // ABI-encoded receiver address
         data: abi.encode(text), // ABI-encoded string
         tokenAmounts: new Client.EVMTokenAmount[](0), // Empty array — no tokens are being sent
         extraArgs: ExtraArgsCodec._getBasicEncodedExtraArgsV3(
           200_000, // Gas limit for the callback on the destination chain
           bytes4(0) // Default finality (wait for full finalization)
         ),
         feeToken: address(s_linkToken) // Pay CCIP fees in LINK
       });

       // Get the fee required to send the message
       uint256 fees = s_router.getFee(destinationChainSelector, evm2AnyMessage);

       if (fees > s_linkToken.balanceOf(address(this))) {
         revert NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
       }

       // Approve the Router to transfer LINK tokens on contract's behalf. It will spend the fees in LINK
       s_linkToken.approve(address(s_router), fees);

       // Send the message through the router and store the returned message ID
       messageId = s_router.ccipSend(destinationChainSelector, evm2AnyMessage);

       // Emit an event with message details
       emit MessageSent(messageId, destinationChainSelector, receiver, text, address(s_linkToken), fees);

       // Return the message ID
       return messageId;
     }
   }
   ```

5. Copy the content of the [Receiver contract](https://docs.chain.link/samples/CCIP/Receiver.sol) into a new file in the workspace.

   ```sol
   // SPDX-License-Identifier: MIT
   pragma solidity 0.8.24;

   import {CCIPReceiver} from "@chainlink/contracts-ccip/contracts/applications/CCIPReceiver.sol";
   import {Client} from "@chainlink/contracts-ccip/contracts/libraries/Client.sol";
   import {FinalityCodec} from "@chainlink/contracts-ccip/contracts/libraries/FinalityCodec.sol";

   /**
    * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
    * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
    * DO NOT USE THIS CODE IN PRODUCTION.
    */

   /// @title - A simple contract for receiving string data across chains.
   contract Receiver is CCIPReceiver {
     event MessageReceived(bytes32 indexed messageId, uint64 indexed sourceChainSelector, address sender, string text);

     bytes32 private s_lastReceivedMessageId;
     string private s_lastReceivedText;

     /// @notice Constructor initializes the contract with the router address.
     /// @param router The address of the router contract.
     constructor(
       address router
     ) CCIPReceiver(router) {}

     /// @notice Handle a received message.
     function _ccipReceive(
       Client.Any2EVMMessage memory any2EvmMessage
     ) internal override {
       s_lastReceivedMessageId = any2EvmMessage.messageId;
       s_lastReceivedText = abi.decode(any2EvmMessage.data, (string));

       emit MessageReceived(
         any2EvmMessage.messageId,
         any2EvmMessage.sourceChainSelector,
         abi.decode(any2EvmMessage.sender, (address)),
         abi.decode(any2EvmMessage.data, (string))
       );
     }

     /// @notice Returns the CCVs and finality config for a given source chain.
     /// @dev Override to advertise receiver finality policy to the OffRamp.
     function getCCVsAndFinalityConfig(
       uint64,
       bytes calldata
     )
       external
       view
       override
       returns (
         address[] memory requiredCCVs,
         address[] memory optionalCCVs,
         uint8 optionalThreshold,
         bytes4 allowedFinalityConfig
       )
     {
       return (new address[](0), new address[](0), 0, FinalityCodec.WAIT_FOR_FINALITY_FLAG);
     }

     /// @notice Fetches the details of the last received message.
     /// @return messageId The ID of the last received message.
     /// @return text The last received text.
     function getLastReceivedMessageDetails() external view returns (bytes32 messageId, string memory text) {
       return (s_lastReceivedMessageId, s_lastReceivedText);
     }
   }
   ```

At this point, you should have three files in your workspace:

- **TestCCIPLocalSimulator.sol**: The file imports the Chainlink Chainlink Local simulator contract.

- **Sender.sol**: The file contains the Sender contract that interacts with CCIP to send data to the Receiver contract.

- **Receiver.sol**: The file contains the Receiver contract that receives data from the Sender contract.

## Deploy the contracts

1. Compile the contracts.

2. Under the **Deploy & Run Transactions** tab, make sure *Remix VM* is selected in the **Environment** drop-down list. Remix will use a sandbox blockchain in the
   browser to deploy the contracts.

3. Deploy the [Chainlink Local simulator](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#cciplocalsimulator):
   1. Select the `TestCCIPLocalSimulator.sol` file in the file explorer.
   2. In the *Contract* drop-down list, select `CCIPLocalSimulator`.
   3. Click the **Deploy** button.
   4. The `CCIPLocalSimulator` is shown in the **Deployed Contracts** section.
   5. In the list of functions, click the [`configuration`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#configuration) function to retrieve the
      configuration details for the pre-deployed contracts and services needed for local CCIP simulations:

      (Image: Image)

4. You will interact with the LINK token contract to fund the sender contract with LINK tokens. The LINK token contract is pre-deployed in the local simulator
   configuration, so you can simply load the LINK token contract instance:

   1. Select `LinkToken` in the *Contract* drop-down list.
   2. Fill in the *At Address* field with the address of the `LINK` token contract from the `CCIPLocalSimulator` configuration.
   3. Click the **At Address** button.

   The `LinkToken` contract is shown in the **Deployed Contracts** section.

5. Deploy the `Sender.sol` contract:

   1. Select the `Sender.sol` file in the file explorer.
   2. In the *Contract* drop-down list, select `Sender`.
   3. Under the *Deploy* section, fill in the constructor parameters:
      - `_router`: The address of the `sourceRouter` contract from the `CCIPLocalSimulator` configuration.
      - `_link`: The address of the `LINK` token contract from the `CCIPLocalSimulator` configuration.
   4. Click the **Deploy** button.

   The `Sender` contract is shown in the **Deployed Contracts** section.

6. Deploy the `Receiver.sol` contract:

   1. Select the `Receiver.sol` file in the file explorer.
   2. In the *Contract* drop-down list, select `Receiver`.
   3. Under the *Deploy* section, fill in the constructor parameters:
      - `_router`: The address of the `destinationRouter` contract from the `CCIPLocalSimulator` configuration.
   4. Click the **Deploy** button.

   The `Receiver` contract is shown in the **Deployed Contracts** section.

## Transfer data from the sender to the receiver

1. Fund the sender contract with LINK tokens to pay for CCIP fees:
   1. Copy the address of the `Sender` contract from the **Deployed Contracts** section.
   2. In the `CCIPLocalSimulator` contract, fill in the [`requestLinkFromFaucet`](/chainlink-local/api-reference/v0.2.3/ccip-local-simulator#requestlinkfromfaucet)
      function with the following inputs:
      - `to`: The address of the `Sender` contract.
      - `amount`: The amount of LINK tokens to transfer. For instance: 1000000000000000000.
   3. Click the **Transact** button.

2. Send data from the sender contract to the receiver contract:
   1. Copy the address of the `Receiver` contract from the **Deployed Contracts** section.

   2. In the `Sender` contract, fill in the `sendMessage` function with:
      - `destinationChainSelector`: The destination chain selector. You can find it in the `CCIPLocalSimulator` configuration.
      - `receiver`: The address of the `Receiver` contract.
      - `text`: The text to send. For instance Hello!.

   3. Remix IDE fails to estimate the gas properly for the `sendMessage` function. To work around this, you need to set the gas limit manually
      to 3000000:

      (Image: Image)

   4. Click the **Transact** button.

3. Check the receiver contract to verify the data transfer:
   1. In the `Receiver` contract, click on the `getLastReceivedMessageDetails` function.
   2. The `getLastReceivedMessageDetails` function returns the text sent from the `Sender` contract:

      (Image: Image)

## Next steps

You have successfully tested the CCIP getting started guide within a few minutes using Remix IDE. Testing locally is useful to debug your contracts and fix any
issues before testing them on testnets, saving you time and resources. As an exercise, you can try any of the
[CCIP guides](/ccip/tutorials) using the local simulator in Remix IDE.