{"_id":"@namestone/ezccip","_rev":"4-fbb582865225a977c3b2968a5538b78e","name":"@namestone/ezccip","dist-tags":{"latest":"0.1.2"},"versions":{"0.0.23":{"name":"@namestone/ezccip","version":"0.0.23","keywords":["ENS","Ethereum Naming System","Ethereum","EIP-3668","ENSIP-10","CCIP","CCIP-Read","Offchain"],"author":{"name":"raffy.eth"},"license":"MIT","_id":"@namestone/ezccip@0.0.23","maintainers":[{"name":"dsinghbailey","email":"darianbailey@gmail.com"},{"name":"slobodnik","email":"alex@namestone.xyz"},{"name":"adraffy","email":"raffy@me.com"}],"homepage":"https://github.com/namestonehq/ezccip.js#readme","bugs":{"url":"https://github.com/namestonehq/ezccip.js/issues"},"dist":{"shasum":"4fc7f7b3e9f928cb00da7d3382421f675dd6438f","tarball":"https://registry.npmjs.org/@namestone/ezccip/-/ezccip-0.0.23.tgz","fileCount":11,"integrity":"sha512-AFFYG6M/wPRZ/9vWAbsTG5YxtaWiTlhirPKRQDJgZJZxb/11Slza6WvSvcc8xbgfQhjQlhHlbQC5SRASNauneg==","signatures":[{"sig":"MEUCIQDlctsxCwAUSB1X8v1noXJxBUhDOhTpXBF8nKEuQbK27wIgKzJc0CpqKX65ZD/NMJ315J06RGYntXdfzyteBHEyh58=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45443},"type":"module","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./serve":{"import":"./dist/serve.mjs","require":"./dist/serve.cjs"}},"gitHead":"a18ec0e370496a0abfcbf234b0a2433e4480adfd","scripts":{"test":"node test/all.js","build":"node build/make.js","start":"node test/demo.js"},"_npmUser":{"name":"dsinghbailey","email":"darianbailey@gmail.com"},"repository":{"url":"git+https://github.com/namestonehq/ezccip.js.git","type":"git"},"_npmVersion":"10.9.2","description":"Turnkey CCIP-Read Handler","directories":{},"_nodeVersion":"22.13.0","dependencies":{"ethers":"^6.13.4"},"_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/ezccip_0.0.23_1738596465993_0.792245573891714","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@namestone/ezccip","version":"0.1.0","keywords":["ENS","Ethereum Naming System","Ethereum","EIP-3668","ENSIP-10","CCIP","CCIP-Read","Offchain"],"author":{"name":"raffy.eth"},"license":"MIT","_id":"@namestone/ezccip@0.1.0","maintainers":[{"name":"dsinghbailey","email":"darianbailey@gmail.com"},{"name":"slobodnik","email":"alex@namestone.xyz"},{"name":"adraffy","email":"raffy@me.com"}],"homepage":"https://github.com/namestonehq/ezccip.js#readme","bugs":{"url":"https://github.com/namestonehq/ezccip.js/issues"},"dist":{"shasum":"a90a0e213f785cbfd7dc2c183970bab4a1b94cce","tarball":"https://registry.npmjs.org/@namestone/ezccip/-/ezccip-0.1.0.tgz","fileCount":11,"integrity":"sha512-SQdpp8r/U0YL1w0zE6Ne8yVOBf0sV2Pw8HQXhsF7sKlMsgFbhq9d0crJ2H4l+N9ALIhvm340jQWlSCT8zRzAWg==","signatures":[{"sig":"MEQCIQCuNkM4l7ff5xSqlgD9LXLF9VEUFdn9Zuz3M5T8sUvbSQIfKobEwnMdWTEhiR9ATQNwZPc8VPm2S7PSKZq98H7DMQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45572},"type":"module","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./serve":{"import":"./dist/serve.mjs","require":"./dist/serve.cjs"}},"gitHead":"62ad329a77b3d5c79ae7ed2a955c22f8e1bb4e1c","scripts":{"test":"node test/all.js","build":"node build/make.js","start":"node test/demo.js"},"_npmUser":{"name":"adraffy","email":"raffy@me.com"},"repository":{"url":"git+https://github.com/namestonehq/ezccip.js.git","type":"git"},"_npmVersion":"11.0.0","description":"Turnkey CCIP-Read Handler","directories":{},"_nodeVersion":"23.1.0","dependencies":{"ethers":"^6.13.4"},"_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.24.0"},"_npmOperationalInternal":{"tmp":"tmp/ezccip_0.1.0_1740685872680_0.8461438283926976","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@namestone/ezccip","version":"0.1.1","keywords":["ENS","Ethereum Naming System","Ethereum","EIP-3668","ENSIP-10","CCIP","CCIP-Read","Offchain"],"author":{"name":"raffy.eth"},"license":"MIT","_id":"@namestone/ezccip@0.1.1","maintainers":[{"name":"dsinghbailey","email":"darianbailey@gmail.com"},{"name":"slobodnik","email":"alex@namestone.xyz"},{"name":"adraffy","email":"raffy@me.com"}],"homepage":"https://github.com/namestonehq/ezccip.js#readme","bugs":{"url":"https://github.com/namestonehq/ezccip.js/issues"},"dist":{"shasum":"a9aaf3f80ea9acf1607eabc45817af9c61a06502","tarball":"https://registry.npmjs.org/@namestone/ezccip/-/ezccip-0.1.1.tgz","fileCount":11,"integrity":"sha512-MZ0pLyAT695OfbXGIKNqHRHylIsk9KHMhpRP3ZgHVJxv5TZi1ouptylfEYr05qDqX33wYnR1w9BIqvcYcqruvA==","signatures":[{"sig":"MEYCIQDn1/3Ic6SKJQKlcDn/lrj5nHxEhkX1gK1XYLvtFnc/tQIhANcO/j2DYvJ1q4FqiwqVZOsAk10TN5LIsjQ9um3ZkL6M","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":47612},"type":"module","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./serve":{"import":"./dist/serve.mjs","require":"./dist/serve.cjs"}},"gitHead":"a8c99841e149c0e5761c81cb3587addaba237c35","scripts":{"test":"node test/all.js","build":"node build/make.js","start":"node test/demo.js"},"_npmUser":{"name":"adraffy","actor":{"name":"adraffy","type":"user","email":"raffy@me.com"},"email":"raffy@me.com"},"repository":{"url":"git+https://github.com/namestonehq/ezccip.js.git","type":"git"},"_npmVersion":"11.3.0","description":"Turnkey CCIP-Read Handler","directories":{},"_nodeVersion":"24.1.0","dependencies":{"ethers":"^6.13.4"},"_hasShrinkwrap":false,"devDependencies":{"esbuild":"^0.25.5"},"_npmOperationalInternal":{"tmp":"tmp/ezccip_0.1.1_1752108534252_0.03338078070310435","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@namestone/ezccip","version":"0.1.2","type":"module","scripts":{"test":"node test/all.js","start":"node test/demo.js","build":"node build/make.js"},"dependencies":{"ethers":"^6.15.0"},"devDependencies":{"esbuild":"^0.25.5"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./serve":{"import":"./dist/serve.mjs","require":"./dist/serve.cjs"}},"repository":{"type":"git","url":"git+https://github.com/namestonehq/ezccip.js.git"},"bugs":{"url":"https://github.com/namestonehq/ezccip.js/issues"},"homepage":"https://github.com/namestonehq/ezccip.js#readme","author":{"name":"raffy.eth"},"license":"MIT","description":"Turnkey CCIP-Read Handler","keywords":["ENS","Ethereum Naming System","Ethereum","EIP-3668","ENSIP-10","CCIP","CCIP-Read","Offchain"],"gitHead":"65213ea84772c24c74c7c82e57cba62112f81fb8","_id":"@namestone/ezccip@0.1.2","_nodeVersion":"24.5.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-yVzNmGmrdk4YG1SBvz/rhmiG4ijsUmJox2ihP1wF2COw6c2rY7l29x40plxn8bC5OgvVKnE8o9KKF65Q/Qt4JA==","shasum":"0722d046d8b4fb999b5d2073d9d1dd8c887f99b5","tarball":"https://registry.npmjs.org/@namestone/ezccip/-/ezccip-0.1.2.tgz","fileCount":11,"unpackedSize":49449,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDQ4EkZ4Yk2na1crv9M38yOqtHKu0Tm0H47IjQeK8FkgAIgIxIYCfef5C2WyWT4534vx+Ww5VFOkbWz1DuLs5ke+Q8="}]},"_npmUser":{"name":"adraffy","email":"raffy@me.com"},"directories":{},"maintainers":[{"name":"dsinghbailey","email":"darianbailey@gmail.com"},{"name":"slobodnik","email":"alex@namestone.xyz"},{"name":"adraffy","email":"raffy@me.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ezccip_0.1.2_1764369253778_0.38877250967622046"},"_hasShrinkwrap":false}},"time":{"created":"2025-02-03T15:27:45.931Z","modified":"2025-11-28T22:34:14.241Z","0.0.23":"2025-02-03T15:27:46.222Z","0.1.0":"2025-02-27T19:51:12.854Z","0.1.1":"2025-07-10T00:48:54.442Z","0.1.2":"2025-11-28T22:34:13.984Z"},"bugs":{"url":"https://github.com/namestonehq/ezccip.js/issues"},"author":{"name":"raffy.eth"},"license":"MIT","homepage":"https://github.com/namestonehq/ezccip.js#readme","keywords":["ENS","Ethereum Naming System","Ethereum","EIP-3668","ENSIP-10","CCIP","CCIP-Read","Offchain"],"repository":{"type":"git","url":"git+https://github.com/namestonehq/ezccip.js.git"},"description":"Turnkey CCIP-Read Handler","maintainers":[{"name":"dsinghbailey","email":"darianbailey@gmail.com"},{"name":"slobodnik","email":"alex@namestone.xyz"},{"name":"adraffy","email":"raffy@me.com"}],"readme":"# ezccip.js\n\nTurnkey [EIP-3668: CCIP-Read](https://eips.ethereum.org/EIPS/eip-3668) Handler for ENS and arbitrary functions.\n\n`npm i @namestone/ezccip` [&check;](https://www.npmjs.com/package/@namestone/ezccip)\n\n- see [**types**](./dist/index.d.mts) / uses [ethers](https://github.com/ethers-io/ethers.js/)\n- works with any server infrastructure\n  - uses minimal imports for serverless\n- implements multiple protocols:\n  - `\"tor\"` &mdash; [namestonehq/**TheOffchainResolver.sol**](https://github.com/namestonehq/TheOffchainResolver.sol)\n  - `\"ens\"` &mdash; [ensdomains/**offchain-resolver**](https://github.com/ensdomains/offchain-resolver/) and [ccip.tools](https://ccip.tools/)\n  - `\"raw\"` &mdash; raw response (EVM Gateway, testing, etc.)\n- used by [namestonehq/**TheOffchainGateway.js**](https://github.com/namestonehq/TheOffchainGateway.js)\n- `enableENSIP10()` drop-in support for [namestonehq/**enson.js**](https://github.com/namestonehq/enson.js) **Record**-type\n- supports _Multicall-over-CCIP-Read_\n  - `resolve(name, multicall([...]))`\n  - `multicall([resolve(name, ...), ...])`\n  - `multicall([resolve(name, multicall([...])), ...])`\n- use [`serve()`](#serve) to quickly launch a server\n- [**CCIP Postman**](https://namestonehq.github.io/ezccip.js/test/postman.html) ⭐️\n  - directly debug any CCIP-Read server (no RPC)\n\n## Demo\n\n1. `npm run start` &mdash; starts a CCIP-Read server for [**TOR**](https://github.com/namestonehq/TheOffchainResolver.sol#context-format) protocol using [`serve()`](#serve)\n1. check [Postman](https://namestonehq.github.io/ezccip.js/test/postman.html#endpoint=https%3A%2F%2Fraffy.xyz%2Fezccip%2F&proto=tor&name=raffy.eth&multi=inner&field=addr-&field=text-description) &larr; change to `http://localhost:8016`\n1. choose a TOR:\n   1. [**TOR** on Mainnet or Sepolia](https://github.com/namestonehq/TheOffchainResolver.sol#theoffchainresolversol)\n   1. [**DNSTORWithENSProtocol** on Mainnet or Sepolia](https://github.com/namestonehq/TheOffchainResolver.sol?tab=readme-ov-file#dnstorwithensprotocolsol)\n1. [setup](https://github.com/namestonehq/TheOffchainResolver.sol#setup) Context: `0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd http://localhost:8016`\n\n### Examples\n\n- **DNS** (using `\"tor\"` protocol on Mainnet): [`ezccip.raffy.xyz`](https://adraffy.github.io/ens-normalize.js/test/resolver.html#ezccip.raffy.xyz)\n  - Resolver: [`0x7CE6Cf740075B5AF6b1681d67136B84431B43AbD`](https://etherscan.io/address/0x7CE6Cf740075B5AF6b1681d67136B84431B43AbD)\n  - Context: `0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd https://raffy.xyz/ezccip/0x7CE6Cf740075B5AF6b1681d67136B84431B43AbD`\n- **ENS** (using `\"tor\"` protocol on Sepolia): [`ezccip.eth`](https://adraffy.github.io/ens-normalize.js/test/resolver.html?sepolia#ezccip.eth)\n  - Resolver: [`0x3c187BAb6dC2C94790d4dA5308672e6F799DcEC3`](https://sepolia.etherscan.io/address/0x3c187BAb6dC2C94790d4dA5308672e6F799DcEC3)\n  - Context: `0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd https://raffy.xyz/ezccip/0x3c187BAb6dC2C94790d4dA5308672e6F799DcEC3`\n- **DNS** (using `\"ens\"` protocol on Mainnet) [`ens.ezccip.raffy.xyz`](https://adraffy.github.io/ens-normalize.js/test/resolver.html#ens.ezccip.raffy.xyz)\n  - Resolver: [0x3CA097Edd180Ea2C2436BD30c021Ca20869087a0](https://etherscan.io/address/0x3CA097Edd180Ea2C2436BD30c021Ca20869087a0)\n  - Contect: `0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd https://raffy.xyz/ezccip/0x3CA097Edd180Ea2C2436BD30c021Ca20869087a0`\n\n## Usage\n\nCreate an instance and register some handlers.\n\n```js\nimport { EZCCIP } from \"@namestone/ezccip\";\n\nlet ezccip = new EZCCIP();\n\n// implement an arbitrary function\nezccip.register(\"add(uint256, uint256) returns (uint256)\", ([a, b]) => [a + b]);\n\n// implement a wildcard ENSIP-10 resolver\n// which handles resolve() automatically\nezccip.enableENSIP10(async (name, context) => {\n  return {\n    async text(key) {\n      switch (key) {\n        case \"name\":\n          return \"Raffy\";\n        case \"avatar\":\n          return \"https://raffy.antistupid.com/ens.jpg\";\n      }\n    },\n  };\n});\n\n// more complicated example\nlet abi = new ethers.Interface([\n  \"function f(bytes32 x) returns (string)\",\n  \"function g(uint256 a, uint256 b) returns (uint256)\",\n]);\nezccip.register(abi, {\n  // register multiple functions at once using existing ABI\n  async [\"f(bytes32)\"]([x], context, history) {\n    // match function by signature\n    history.show = [context.sender]; // replace arguments of f(...) in logger\n    history.name = \"Chonk\"; // rename f() to Chonk() in logger\n    return [context.calldata]; // echo incoming calldata\n  },\n  async [\"0xe2179b8e\"]([a, b], context) {\n    // match by selector\n    context.protocol = \"tor\"; // override signing protocol\n    context.signingKey = ...; // override signing key\n    return ethers.toBeHex(1337n, 32); // return raw encoded result\n  },\n});\n```\n\nWhen your server has a request for CCIP-Read, use EZCCIP to produce a response.\n\n```js\nlet { sender, data: calldata } = JSON.parse(req.body); // ABI-encoded request in JSON from EIP-3668\nlet { data, history } = await ezccip.handleRead(sender, calldata, {\n  protocol: \"tor\", // default, tor requires signingKey + resolver\n  signingKey, // your private key\n});\nreply.json({ data }); // ABI-encoded response in JSON for EIP-3668\nconsole.log(history.toString()); // description of response\n```\n\n- implement via `GET`, `POST`, or query directly\n- `context` carries useful information about the incoming request\n- `history` collects information as the response is generated\n\n### serve()\n\nStart a [simple server](./src/serve.js) for an EZCCIP instance or a function representing the `enableENSIP10()` handler.\n\n```js\nimport { serve } from \"@namestone/ezccip/serve\";\nlet ccip = await serve(ezccip); // see types for more configuration\n// ...\nawait ccip.shutdown();\n\n// minimal example:\n// return fixed text() for any name\nawait serve(() => {\n  text: () => \"Raffy\";\n});\n```\n\n#### Sender vs Origin\n\n- ⚠️ `sender` may not be the originating contract\n  - see: [recursive CCIP-Read](https://eips.ethereum.org/EIPS/eip-3668#recursive-calls-in-ccip-aware-contracts)\n- **Best Solution**: embed `origin` into the endpoint as a path component:\n  1.  `http://my.server/.../0xABCD/...`\n  1.  `origin = 0xABCD`\n- or, use `parseOrigin(path: string) => string` to extract `origin` from an arbitrary path\n- or, supply a fallback `origin`\n- if `origin` is not detected, `origin = sender`\n\n### processENSIP10()\n\nApply ENSIP-10 `calldata` to a `Record`-object and generate the corresponding ABI-encoded response. This is a free-function.\n\n```js\nlet record = {\n    text(key) { if (key == 'name') return 'raffy'; }\n    addr(type) { if (type == 60) return '0x1234'; }\n};\nlet calldata = '0x...'; // encodeFunctionData('text', ['name']);\nlet res = await processENSIP10(record, calldata); // encodeFunctionResult('text', ['raffy']);\n```\n","readmeFilename":"README.md"}