# CKB Provider JavaScript API ## 摘要 这个 RFC 定义了 CKB Provider JavaScript API,让不同的应用使用同样的 API 通过 Provider 与 CKB 节点进行交互。 ## 名词解释 ### Wallet 用来管理用户的公钥和私钥,为交易签名。 ### Provider Provider 可以看做是一组 API,用来方便应用与区块链进行交互。一方面可以代替应用向 CKB 节点发送 RPC 请求,就好像互联网服务提供商(Provider)一样,为应用提供了访问互联网的能力,而区块链的 Provider 为应用提供了访问区块链的能力。另一方面,除了访问区块链,Provider 本身还可以提供额外的功能,比如返回用户的钱包地址、签名等。通常 Provider 会内置在 Wallet 中,也可以是一个单独的库。 ### CKB 节点 CKB 节点是实现了 CKB 协议的客户端,也称为区块链节点,接收来自 Provider 或者用户 的 Remote Procedure Call (RPC),并返回结果。 ### 应用(Application) 面向用户的程序,通常具有用户界面,允许用户与其交互,并返回特定的结果。 ## 动机 不同的 Provider 会定义不同的 API 名称,这样会导致定义和交互方式的碎片化,而通过定义一个统一的接口可以避免这类问题,给应用开发者更好的开发体验。例如,对于一个浏览器插件钱包,钱包会提供 API 给 DApp 开发者使用,如: ``` getAddresses: any[]; // 获取可用的地址列表 ``` 而另外一个桌面端钱包也提供了类似的 API: ``` methodCall({ method: "getAddressList", pubKey: string }): { message: string, result: any[] }; // 获取可用的地址列表 ``` 虽然这两个 API 的功能类似,但却使用了不同的调用方式和返回的数据结构。 通过使用 [JSON RPC](jsonrpc.org/specification) 的方式可以将不同的 API 调用方式统一起来: ``` // getAddresses --> {"jsonrpc": "2.0", "method": "getAddresses", "params": [], "id": 1} <-- {"jsonrpc": "2.0", "result": [], "id": 1} // getAddressList --> {"jsonrpc": "2.0", "method": "getAddressList", "params": ["0x11"], "id": 2} <-- {"jsonrpc": "2.0", "result": [], "id": 2} ``` 这样就提供了几个优点: * 提供统一的调用方式。无论是对于 CKB 节点的直接 [RPC](https://github.com/nervosnetwork/ckb/tree/develop/rpc) 调用,还是调用 Provider 提供的 RPC 方法,调用方法都是一样的,应用开发者不需要进行区分。 * 兼顾了接口统一和灵活扩展。开发者在使用统一接口的基础上,通过添加自定义的方法来进行扩展。 ![](https://i.imgur.com/E8xLz5J.png) 但是 JSON RPC 的调用方式构造起来有点复杂,为了简化调用方式,我们提议使用一个统一的 API 名称: `request`。 ## 规范 ### API request: 封装了 JSON RPC 调用,方便用户与区块链进行交互。 ``` interface Request { method: string; params?: Array<any>; } interface Result { result: any; error: Error; } interface Error { message: string; code: number; data?: any; } Provider.request(args: Request): Promise<Result>; ``` #### 请求 请求的参数需要符合下面的结构: ``` interface Request { method: string; params?: Array<any>; } Provider.request(args: Request) ``` #### 返回值 ``` interface Result { result: any; error: Error; } ``` * 成功: 返回`result` 由具体的方法指定。 * 失败: 返回 `error` 符合下面 `Error` 的定义。 #### Error 如果发生错误,返回的数据结构需要符合下面的定义: ``` interface Error { message: string; code: number; data?: any; } ``` * message: 人类可读的字符串。 * code: 错误码,正整数。参考下面的 Error Code 的定义。 * data: 任何其它的信息。 #### Error Code | Code | Name | Explanation | | -------- | -------- | -------- | | 1001 | Rejected | Request is rejected by user | | 1004 | UnknownError | Other error | | 1100 | Unauthorized | Method or account is not authorized by user | | 1200 | Unsupported | Method is not supported by provider | Example: ``` import Provider from "ckb-provider"; const provider = new Provider(["http://127.0.0.1:8114"]); const tx = { version: "0x0", cellDeps: [{ outPoint: { // a fixed value for testnet txHash: "0xf8de3bb47d055cdf460d93a2a6e1b05f7432f9777c8c474abf4eec1d4aee5d37", index: "0x0" }, depType: "depGroup", }], headerDeps: [], inputs: [], outputs: [], witnesses: [], outputsData: [] }; const result: Result = await provider.request({ method: "send_transaction", params: [tx] }); ``` ## References * https://eips.ethereum.org/EIPS/eip-1193 * https://ezcook.de/2020/09/03/wallet-auth-rfc * https://www.jsonrpc.org/specification