{"id":17971092,"url":"https://github.com/stevennevins/ccip-server-template","last_synced_at":"2025-04-03T22:15:52.565Z","repository":{"id":246698444,"uuid":"820906989","full_name":"stevennevins/ccip-server-template","owner":"stevennevins","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-11T17:16:34.000Z","size":251,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-27T07:49:49.196Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevennevins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-27T12:17:18.000Z","updated_at":"2024-07-11T17:16:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"4dfac9c7-9560-4b64-ad5e-9eefee6c9b9e","html_url":"https://github.com/stevennevins/ccip-server-template","commit_stats":null,"previous_names":["stevennevins/ccip-server-template"],"tags_count":0,"template":true,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevennevins%2Fccip-server-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevennevins%2Fccip-server-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevennevins%2Fccip-server-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevennevins%2Fccip-server-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevennevins","download_url":"https://codeload.github.com/stevennevins/ccip-server-template/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247086024,"owners_count":20881160,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-29T15:36:45.121Z","updated_at":"2025-04-03T22:15:52.545Z","avatar_url":"https://github.com/stevennevins.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CCIP Server Template\n\n## Purpose\n\nThis repository serves as a template for setting up a Cross-Chain Interoperability Protocol (CCIP) server for implementing [EIP-3668](https://eips.ethereum.org/EIPS/eip-3668).\n\n## Overview\n\nThe Cross-Chain Interoperability Protocol (CCIP) is a standardized method for smart contracts to access off-chain data and services. This repository demonstrates a practical implementation of CCIP, showcasing how smart contracts can interact with off-chain data sources while maintaining the security and transparency of the blockchain.\n\n## Writing a Handler for CCIP Read Server\n\nHandlers are the core components of a CCIP server. They process off-chain requests and return the required data. Here's a step-by-step guide on how to write a handler:\n\n### 1. Implement the Handler Class\n\nCreate a new file for your handler (e.g., `src/handlers/yourHandlerService.ts`) and implement a class that follows the `HandlerDescription` interface:\n\n```typescript\nimport { HandlerDescription, HandlerFunc } from \"@chainlink/ccip-read-server\";\n\nexport class YourHandler implements HandlerDescription {\n  public readonly type: string = \"yourHandlerType\";\n  public readonly func: HandlerFunc;\n\n  constructor() {\n    this.func = async () =\u003e {\n      const result = await this.yourMethod();\n      return [result];\n    };\n  }\n\n  yourMethod = async (): Promise\u003cstring\u003e =\u003e {\n    // Implement your logic here\n    return \"your result\";\n  };\n}\n```\n\n### 2. Write the Verifier Contract\n\nCreate a Solidity contract that includes a function to trigger the off-chain lookup and a callback function to process the result. Below is an example of how to write a verifier contract:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ncontract YourVerifier {\n    error OffchainLookup(\n        address sender,\n        string[] urls,\n        bytes callData,\n        bytes4 callbackFunction,\n        bytes extraData\n    );\n\n    function yourOffchainFunction() external view returns (string memory) {\n        string[] memory urls = new string[](1);\n        urls[0] = \"http://localhost:8000/{sender}/{data}.json\";\n        bytes memory callData = abi.encodeWithSignature(\"yourMethod()\");\n        revert OffchainLookup(\n            address(this),\n            urls,\n            callData,\n            this.yourCallback.selector,\n            \"\"\n        );\n    }\n\n    function yourCallback(\n        bytes calldata result,\n        bytes calldata\n    ) public pure returns (string memory) {\n        return abi.decode(result, (string));\n    }\n}\n```\n\n### 3. Add the Handler to the Gateway\n\nIn your `src/app.ts` file, import your new handler and add it to the server:\n\n```typescript\nimport { Server } from \"@chainlink/ccip-read-server\";\nimport { utils } from \"ethers\";\nimport { YourHandler } from \"./handlers/yourHandlerService\";\nimport { gatewayAbi } from \"./config\";\n\nexport function makeApp(signer: utils.SigningKey, basePath: string) {\n  const server = new Server();\n  const handlers = [new YourHandler()];\n  server.add(gatewayAbi, handlers);\n  return server.makeApp(basePath);\n}\n```\n\n### 4. Update the Gateway ABI\n\nEnsure that your `gatewayAbi` in `src/contracts/IGateway.sol` includes the interface for your new handler. You can update this by adding it to the interface like below:\n\n```solidity\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.0;\n\ninterface IGateway {\n    function yourOffchainFunction() external view returns (string memory);\n}\n```\n\n### 5. Writing Tests for Your Handler\n\nTesting is crucial for ensuring the reliability and correctness of your CCIP handler. Follow these steps to write comprehensive tests:\n\n#### Unit Tests\n\nStart by writing unit tests for your handler. Create a new file in the `test/handlers` directory (e.g., `test/handlers/yourHandler.test.ts`):\n\n```typescript\nimport { YourHandler } from \"../../src/handlers/yourHandlerService\";\n\ndescribe(\"YourHandler\", () =\u003e {\n  let yourHandler: YourHandler;\n\n  beforeAll(() =\u003e {\n    yourHandler = new YourHandler();\n  });\n\n  it(\"should return the correct result\", async () =\u003e {\n    const result = await yourHandler.yourMethod();\n    expect(result).toBe(\"your result\");\n  });\n});\n```\n\n\n#### Server Integration Tests\n\nNext, add integration tests in `test/app.test.ts` to ensure your handler works correctly with the server:\n\n\nAdd a new describe block for your handler:\n\n```typescript\ndescribe(\"yourOffchainFunction\", () =\u003e {\n  it(\"should return correct data\", async () =\u003e {\n    const yourHandlerIface = new ethers.utils.Interface(gatewayAbi);\n    const yourHandlerCalldata = yourHandlerIface.encodeFunctionData(\"yourOffchainFunction\");\n\n    const response = await makeRequest(TEST_ADDR, yourHandlerCalldata);\n    expect(response.status).toBe(200);\n\n    const [result] = yourHandlerIface.decodeFunctionResult(\n      \"yourOffchainFunction\",\n      response.body.data\n    );\n\n    expect(result).toBe(\"your result\");\n  });\n});\n```\n\n#### End-to-End Tests\n\nFinally, create an e2e test file (e.g., `test/e2e.test.ts`) to verify that your contract and server communicate correctly:\n\n```typescript\nimport { ethers } from \"ethers\";\nimport { deployContract, startServer, stopServer } from \"./testUtils\";\n\ndescribe(\"E2E Tests\", () =\u003e {\n  let provider: ethers.providers.JsonRpcProvider;\n  let yourVerifier: ethers.Contract;\n\n  beforeAll(async () =\u003e {\n    await startServer();\n    provider = new ethers.providers.JsonRpcProvider(\"http://localhost:8545\");\n    yourVerifier = await deployContract(\"YourVerifier\", provider);\n  });\n\n  afterAll(async () =\u003e {\n    await stopServer();\n  });\n\n  it(\"should correctly execute yourOffchainFunction\", async () =\u003e {\n    const result = await yourVerifier.yourOffchainFunction();\n    expect(result).toBe(\"your result\");\n  });\n});\n```\n\n## Conclusion\n\nThis template repository provides a fast way to start writing smart contracts that leverage CCIP. It includes a robust end-to-end testing setup to help you integrate these off-chain features efficiently. With pre-configured environment settings, contract compilation utilities, and comprehensive test examples, you can quickly set up your project and focus on developing your smart contract logic.\n\n## Usage\n\n1. Clone the repository.\n\n2. Install dependencies using `npm install`.\n\n3. Install Foundry by following the instructions at https://github.com/foundry-rs/foundry.\n\n4. Run tests using `npm test`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevennevins%2Fccip-server-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevennevins%2Fccip-server-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevennevins%2Fccip-server-template/lists"}