ezccip.js
Turnkey EIP-3668: CCIP-Read Handler for ENS and arbitrary functions.
npm i @resolverworks/ezccip
✓
- see types / uses ethers
- works with any server infrastructure
- uses minimal imports for serverless
- implements multiple protocols:
- used by resolverworks/TheOffchainGateway.js
enableENSIP10()
drop-in support for resolverworks/enson.js Record-type
- supports Multicall-over-CCIP-Read
resolve(name, multicall([...]))
multicall([resolve(name, ...), ...])
multicall([resolve(name, multicall([...])), ...])
- use
serve()
to quickly launch a server
- CCIP Postman ⭐️
- directly debug any CCIP-Read server (no RPC)
Demo
npm run start
— starts a CCIP-Read server for TOR protocol using serve()
setText("ccip.context", "0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd http://localhost:8016")
Examples
- DNS:
ezccip.raffy.xyz
(Mainnet)
- Context:
0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd https://raffy.xyz/ezccip/
- ENS:
ezccip.eth
(Sepolia)
- Context:
0xd00d726b2aD6C81E894DC6B87BE6Ce9c5572D2cd https://raffy.xyz/ezccip/s
Usage
Create an instance and register some handlers.
import {EZCCIP} from '@resolverworks/ezccip';
let ezccip = new EZCCIP();
ezccip.register('add(uint256, uint256) returns (uint256)', ([a, b]) => [a + b]);
ezccip.enableENSIP10(async (name, context) => {
return {
async text(key) {
switch (key) {
case 'name': return 'Raffy';
case 'avatar': return 'https://raffy.antistupid.com/ens.jpg';
}
},
};
});
let abi = new ethers.Interface([
'function f(bytes32 x) return (string)',
'function g(uint256 a, uint256 b) return (uint256)',
]);
ezccip.register(abi, {
async ['f()']([x], context, history) {
history.show = [context.sender];
history.name = 'Chonk';
return [context.calldata];
},
async ['0xe2179b8e']([a, b], context) {
context.protocol = "tor";
return ethers.toBeHex(1337n, 32);
}
});
When your server has a request for CCIP-Read, use EZCCIP to produce a response.
let {sender, data: calldata} = JSON.parse(req.body);
let {data, history} = await ezccip.handleRead(sender, calldata, {
protocol: 'tor',
signingKey,
resolver,
});
reply.json({data});
console.log(history.toString());
- implement via
GET
, POST
, or query directly
context
carries useful information about the incoming request
history
collects information as the response is generated
serve()
Start a simple server for an EZCCIP instance or a function representing the enableENSIP10()
handler.
import {serve} from '@resolverworks/ezccip/serve';
let ccip = await serve(ezccip);
await ccip.shutdown();
await serve(() => { text: () => 'Raffy' });
serve()
will bind requests to the sender
if the protocol needs a target and no resolver
was provided.
- Provide a
resolvers
mapping to pair endpoint suffixes to specific contract deployments.
- The demo uses
s
to correspond to the Sepolia deployment, which makes requests to the modified endpoint http://localhost:8016/s
target that contract, regardless of sender.
- An
endpoint
↔ contract
pairing is required to support wrapped CCIP calls!
processENSIP10()
Apply ENSIP-10 calldata
to a Record
-object and generate the corresponding ABI-encoded response. This is a free-function.
let record = {
text(key) { if (key == 'name') return 'raffy'; }
addr(type) { if (type == 60) return '0x1234'; }
};
let calldata = '0x...';
let res = await processENSIP10(record, calldata);