ethereum/EIPs-1193 Ethereum Provider JavaScript API 如metamask更新后的接口
| eip | title | author | discussions-to | status | type | category | created | requires |
|---|---|---|---|---|---|---|---|---|
|
1193
|
Ethereum Provider JavaScript API
|
Ryan Ghods (@ryanio), Marc Garreau (@marcgarreau)
|
Draft
|
Standards Track
|
Interface
|
2018-06-30
|
1102
|
Summary
This EIP formalizes an Ethereum Provider JavaScript API for consistency across clients and applications.
The provider is designed to be minimal, containing 4 methods: enable, send, subscribe, and unsubscribe. It emits 4 types of events: connect, close, networkChanged, and accountsChanged.
It is intended to be available on window.ethereum.
这个EIP就是之前ethereum/EIPs-1102 Opt-in provider access metamask不再默认直接连入网页的详细接口的实现的解释
API
Enable
By default a "read-only" provider is supplied to allow access to the blockchain while preserving user privacy.
就是为了保证用户的隐私,一个Dapp一开始连接上的provider是只读的状态
A full provider can be requested to allow account-level methods:
如果想要获取full provider的使用权限,得到钱包中账户的信息,并能够进行相应的调用,就要使用ethereum.enable()去请求用户同意连接
ethereum.enable(): Promise<[String]>;
window.addEventListener('load', async () => {
// Read-only provider is exposed by default
console.log(await ethereum.send('net_version'));//能够读取信息
try {
// Request full provider if needed
await ethereum.enable();
// Full provider exposed
await ethereum.send('eth_sendTransaction', [/* ... */]);
} catch (error) {
// User denied full provider access
}
});
Promise resolves with an array of the accounts' public keys, or rejects with Error.
If the dapp has been previously authenticated and remembered by the user, then the provider supplied on load MAYautomatically be enabled with the previously authenticated accounts.就是如果之前有通过身份验证并且被用户所记得,那么provider可能会自动连接上
Send
Ethereum API methods can be sent and received:
成功连接后就能够进行方法的调用了
ethereum.send(method: String, params?: Array<any>): Promise<any>;
Promise resolves with result or rejects with Error.
See the available methods.相关的方法如下所示,详细使用信息看
https://github.com/ethereum/wiki/wiki/JSON-RPC#json-rpc-methods
JSON-RPC methods web3_clientVersion
web3_sha3
net_version
net_peerCount
net_listening
eth_protocolVersion
eth_syncing
eth_coinbase
eth_mining
eth_hashrate
eth_gasPrice
eth_accounts
eth_blockNumber
eth_getBalance
eth_getStorageAt
eth_getTransactionCount
eth_getBlockTransactionCountByHash
eth_getBlockTransactionCountByNumber
eth_getUncleCountByBlockHash
eth_getUncleCountByBlockNumber
eth_getCode
eth_sign
eth_sendTransaction
eth_sendRawTransaction
eth_call
eth_estimateGas
eth_getBlockByHash
eth_getBlockByNumber
eth_getTransactionByHash
eth_getTransactionByBlockHashAndIndex
eth_getTransactionByBlockNumberAndIndex
eth_getTransactionReceipt
eth_getUncleByBlockHashAndIndex
eth_getUncleByBlockNumberAndIndex
eth_getCompilers
eth_compileLLL
eth_compileSolidity
eth_compileSerpent
eth_newFilter
eth_newBlockFilter
eth_newPendingTransactionFilter
eth_uninstallFilter
eth_getFilterChanges
eth_getFilterLogs
eth_getLogs
eth_getWork
eth_submitWork
eth_submitHashrate
db_putString
db_getString
db_putHex
db_getHex
shh_post
shh_version
shh_newIdentity
shh_hasIdentity
shh_newGroup
shh_addToGroup
shh_newFilter
shh_uninstallFilter
shh_getFilterChanges
shh_getMessages
但是你也可以使用如下的方法web3来调用方法:
window.addEventListener('load', async () => {
// Modern dapp browsers...现在的连接方式
if (window.ethereum) {//如果安装了metamask,且登录了,暴露个目前只读的provider;如果没有安装metamask或没有登录,那么window.ethereum将为undefined
window.web3 = new Web3(ethereum); //provider通过ethereum暴露,相当于以前的currentProvider
try {
// Request account access if needed
await ethereum.enable();
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */});//举个调用的例子
} catch (error) {//用户拒绝
// User denied account access...
}
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
});
Subscriptions
Subscribe
ethereum.subscribe(subscriptionType: String, params?: Array<any>): Promise<String>;
Promise resolves with subscriptionId: String (是一个类似于"0x9cef478923ff08bf67fde6c64013158d"的字符串)or rejects with Error.
See the types of subscriptions.
详细信息看:https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB
比如:
Create subscription
Subscriptions are creates with a regular RPC call with eth_subscribe as method and the subscription name as first parameter. If successful it returns the subscription id.
就是使用方法eth_subscribe用于订阅,第一个参数是订阅名,比如"newHeads"即你想知道最新产生的区块头的信息、"logs"即相应的日志信息、"newPendingTransactions"即新生成的正在等待验证的交易信息;第二个参数即一些过滤信息,比如你只想知道与某个账户address相关的logs信息等。如果成功,将会返回订阅id(即满足你的要求的订阅),然后能够通过这个id去查看到详细的返回信息。
Parameters
- subscription name
- optional arguments
Example
>> {"id": , "method": "eth_subscribe", "params": ["newHeads", {"includeTransactions": true}]}
<< {"id": , "jsonrpc": "2.0", "result": "0x9cef478923ff08bf67fde6c64013158d"}
Results emit on subscriptionId using EventEmitter. Attach listeners with:
ethereum.on(subscriptionId, listener: (result: any) => void): this;
The event emits with result, the subscription result or an Error object.
这个的意思就是:比如上面的订阅是想要找到新的区块头并包含Transactions,订阅ID是代表这种要求的订阅,在区块生成的过程中,满足这种订阅条件的区块头是很多的,所以是需要EventEmitter来进行获取的,只要不取消订阅,那么EventEmitter将一直等待满足订阅ID的订阅并触发
比较详细的信息我们就可以通过EventEmitter获得,结果如下:
<< {
"jsonrpc": "2.0",
"method": "eth_subscription",
"params": {
"result": {
"difficulty": "0x15d9223a23aa",
"extraData": "0xd983010305844765746887676f312e342e328777696e646f7773",
"gasLimit": "0x47e7c4",
"gasUsed": "0x38658",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0xf8b483dba2c3b7176a3da549ad41a48bb3121069",
"nonce": "0x084149998194cc5f",
"number": "0x1348c9",
"parentHash": "0x7736fab79e05dc611604d22470dadad26f56fe494421b5b333de816ce1f25701",
"receiptRoot": "0x2fab35823ad00c7bb388595cb46652fe7886e00660a01e867824d3dceb1c8d36",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"stateRoot": "0xb3346685172db67de536d8765c43c31009d0eb3bd9c501c9be3229203f15f378",
"timestamp": "0x56ffeff8",
"transactionsRoot": "0x0167ffa60e3ebc0b080cdb95f7c0087dd6c0e61413140e39d94d3468d7c9689f"
},
"subscription": "0x9ce59a13059e417087c02d3236a0b1cc"
}
}
Unsubscribe取消订阅
ethereum.unsubscribe(subscriptionId: String): Promise<Boolean>;
Promise resolves with success: Boolean or rejects with Error.
All EventEmitter listeners on subscriptionId will also be removed.
当用户不想要再得到满足订阅ID的某种订阅后,就可以取消订阅,相应的EventEmitter也会停下来
Events
Events are emitted using EventEmitter.
connect
The provider emits connect on connect to a network.就是当你钱包连接上了网络就会触发这个connect事件
ethereum.on('connect', listener: () => void): this;
You can detect which network by sending net_version:
const network = await ethereum.send('net_version');
> ''
close
The provider emits close on disconnect from a network.当你钱包断开网络连接
ethereum.on('close', listener: (code: Number, reason: String) => void): this;
The event emits with code and reason. The code follows the table of CloseEvent status codes.
networkChanged
The provider emits networkChanged on connect to a new network.当你钱包中换了一个新网络进行连接
ethereum.on('networkChanged', listener: (networkId: String) => void): this;
The event emits with networkId, the new network returned from net_version.
accountsChanged
The provider emits accountsChanged if the accounts returned from the provider (eth_accounts) changes.当你钱包更换了账户
ethereum.on('accountsChanged', listener: (accounts: Array<String>) => void): this;
The event emits with accounts, an array of the accounts' public keys.
Constructor
ethereum.constructor.name;
> 'EthereumProvider'
Examples
const ethereum = window.ethereum;
//这是两种使用方法,一种即将EthereumProvider设置到web3上,然后之后就能够使用web3的接口来调用方法,如web3.eth.sendTransaction({/* ... */});
// A) Primary use case - set provider in web3.js
web3.setProvider(ethereum);
//不然另一种方法就是直接使用ethereum,然后用本文上面的方式来进行方法的调用,即send\subscribe等
// B) Secondary use case - use provider object directly
// Example 1: Log last block
ethereum
.send('eth_getBlockByNumber', ['latest', 'true'])//得到最新的区块账户信息
.then(block => {
console.log(`Block ${block.number}:\n${block}`);
})
.catch(error => {
console.error(
`Error fetching last block: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
}); // Example 2: Enable full provider
ethereum
.enable()
.then(accounts => {
console.log(`Enabled accounts:\n${accounts.join('\n')}`);//得到钱包中所有的账户信息
})
.catch(error => {
console.error(
`Error enabling provider: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
}); // Example 3: Log available accounts
ethereum
.send('eth_accounts')
.then(accounts => {
console.log(`Accounts:\n${accounts.join('\n')}`);//得到钱包中所有的账户信息
})
.catch(error => {
console.error(
`Error fetching accounts: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
});
} // Example 4: Log new blocks
let subId;//全局变量,用于之后取消订阅
ethereum
.subscribe('newHeads')//订阅得到新区块头的信息
.then(subscriptionId => {
subId = subscriptionId;
ethereum.on(subscriptionId, block => {
if (result instanceOf Error) {
const error = result;
console.error(
`Error from newHeads subscription: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
} else {
console.log(`New block ${block.number}:\n${block}`);
}
});
})
.catch(error => {
console.error(
`Error making newHeads subscription: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
});
// to unsubscribe
ethereum
.unsubscribe(subId)//取消订阅
.then(result => {
console.log(`Unsubscribed newHeads subscription ${subscriptionId}`);
})
.catch(error => {
console.error(
`Error unsubscribing newHeads subscription: ${error.message}.
Code: ${error.code}. Data: ${error.data}`
);
}); // Example 5: Log when accounts change
const logAccounts = accounts => {
console.log(`Accounts:\n${accounts.join('\n')}`);
};
ethereum.on('accountsChanged', logAccounts);//账户更改的话则调用回调logAccounts
// to unsubscribe
ethereum.removeListener('accountsChanged', logAccounts); // Example 6: Log if connection ends
ethereum.on('close', (code, reason) => {
console.log(
`Ethereum provider connection closed: ${reason}. Code: ${code}`
);
});
Class
The name of the constructor of the Ethereum Provider MUST be EthereumProvider.
web3.js Provider
The implementing Ethereum Provider MUST be compatible as a web3.js provider. This is accomplished by providing two methods in the EthereumProvider:
sendAsync(payload: Object, callback: (error: any, result: any) => void): void isConnected(): Boolean.
Ethereum Provider与web3.js provider兼容是通过两个函数实现的,详情如下代码所示
Error object and codes
If an Error object is returned, it MUST contain a human readable string message describing the error and SHOULDpopulate the code and data properties on the error object with additional error details.
Appropriate error codes SHOULD follow the table of CloseEvent status codes, along with the following table:
| Status code | Name | Description |
|---|---|---|
| 4001 | User Denied Full Provider | User denied the enabling of the full Ethereum Provider by choosing not to authorize any accounts for the dapp. |
Sample Class Implementation
class EthereumProvider extends EventEmitter {
constructor() {
// Call super for `this` to be defined
super();
// Init storage
this._isConnected = false;
this._nextJsonrpcId = ;
this._promises = {};
this._activeSubscriptions = [];
// Fire the connect
this._connect();
// Listen for jsonrpc responses
window.addEventListener('message', this._handleJsonrpcMessage.bind(this));
}
/* Methods */
enable() {
return new Promise((resolve, reject) => {
window.mist
.requestAccounts()
.then(resolve)
.catch(reject);
});
}
send(method, params = []) {
if (!method || typeof method !== 'string') {
return new Error('Method is not a valid string.');
}
if (!(params instanceof Array)) {
return new Error('Params is not a valid array.');
}
const id = this._nextJsonrpcId++;
const jsonrpc = '2.0';
const payload = { jsonrpc, id, method, params };
const promise = new Promise((resolve, reject) => {
this._promises[payload.id] = { resolve, reject };
});
// Send jsonrpc request to Mist
window.postMessage(
{ type: 'mistAPI_ethereum_provider_write', message: payload },
origin
);
return promise;
}
subscribe(subscriptionType, params) {
return this.send('eth_subscribe', [subscriptionType, ...params]).then(
subscriptionId => {
this._activeSubscriptions.push(subscriptionId);
}
);
}
unsubscribe(subscriptionId) {
return this.send('eth_unsubscribe', [subscriptionId]).then(success => {
if (success) {
// Remove subscription
this._activeSubscription = this._activeSubscription.filter(
id => id !== subscriptionId
);
// Remove listeners on subscriptionId
this.removeAllListeners(subscriptionId);
}
});
}
/* Internal methods */
_handleJsonrpcMessage(event) {
// Return if no data to parse
if (!event || !event.data) {
return;
}
let data;
try {
data = JSON.parse(event.data);
} catch (error) {
// Return if we can't parse a valid object
return;
}
// Return if not a jsonrpc response
if (!data || !data.message || !data.message.jsonrpc) {
return;
}
const message = data.message;
const { id, method, error, result } = message;
if (typeof id !== 'undefined') {
const promise = this._promises[id];
if (promise) {
// Handle pending promise
if (data.type === 'error') {
promise.reject(message);
} else if (message.error) {
promise.reject(error);
} else {
promise.resolve(result);
}
delete this._promises[id];
}
} else {
if (method && method.indexOf('_subscription') > -) {
// Emit subscription result
const { subscription, result } = message.params;
this.emit(subscription, result);
}
}
}
/* Connection handling */
_connect() {
// Send to Mist
window.postMessage({ type: 'mistAPI_ethereum_provider_connect' }, origin);
// Reconnect on close
this.once('close', this._connect.bind(this));
}
/* Events */
_emitConnect() {
this._isConnected = true;
this.emit('connect');
}
_emitClose(code, reason) {
this._isConnected = false;
this.emit('close', code, reason);
// Send Error objects to any open subscriptions
this._activeSubscriptions.forEach(id => {
const error = new Error(
`Provider connection to network closed.
Subscription lost, please subscribe again.`
);
this.emit(id, error);
});
// Clear subscriptions
this._activeSubscriptions = [];
}
_emitNetworkChanged(networkId) {
this.emit('networkChanged', networkId);
}
_emitAccountsChanged(accounts) {
this.emit('accountsChanged', accounts);
}
/* web3.js provider compatibility */
sendAsync(payload, callback) {
return this.send(payload.method, payload.params)
.then(result => {
const response = payload;
response.result = result;
callback(null, response);
})
.catch(error => {
callback(error, null);
// eslint-disable-next-line no-console
console.error(
`Error from EthereumProvider sendAsync ${payload}: ${error}`
);
});
}
isConnected() {
return this._isConnected;
}
}
ethereum/EIPs-1193 Ethereum Provider JavaScript API 如metamask更新后的接口的更多相关文章
- 【原创】web端高德地图javascript API的调用
关于第三放地图的使用,腾讯.百度.高德 具体怎么选择看你自己怎么选择了. 高德地图开放平台:http://lbs.amap.com/ 本次使用的是高德的javascript API http://lb ...
- ABP理论学习之Javascript API(理论完结篇)
返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...
- 使用ArcGIS JavaScript API 3.18 加载天地图
对于中国开发者在创建GIS应用的时候,往往比较头疼的是底图资源的缺乏.其实国家测绘地信局就提供一个很好的免费资源:天地图.使用ArcGIS API的开发人员可以直接利用该资源作为地图应用的底图. Ar ...
- 如何正确响应ArcGIS JavaScript API中图形的鼠标事件
在使用ArcGIS JavaScript API编写程序的时候,程序员往往需要完成这样一个功能:点击地图上的图形,自动进行专题GIS数据查询,当在地图非图形区域上点击时,自动进行底图兴趣点查询. 由于 ...
- libj 0.8.2 发布,Java/JavaScript API 的 C++ 实现
libj 0.8.2 增加了一些新的字符串相关的方法. libj 是一个跨平台的运行库,相当于提供了类似 Java/JavaScript API.libj 的内存管理是自动的,基于 shared_pt ...
- 百度地图JavaScript API覆盖物旋转时出现偏移
在项目中,调用百度地图JavaScript API,做覆盖物的旋转再添加到地图上,结果出现偏移了. 调试过程中的效果图: 发现图片的旋转并不是按车子的中心来的,而是之外的一个点.最后发现犯了一个很细节 ...
- 百度API使用--javascript api进行多点定位
使用百度地图提供的javascript api,给定多点的经纬度坐标,在百度地图上 显示这些坐标点. 其中包括各个点自适应地图显示,自定义坐标点的图标,以及各个点之间添加折线. 实现的效果如下图: 具 ...
- 专门用于微信公众平台的Javascript API
1 /**! 2 * 微信内置浏览器的Javascript API,功能包括: 3 * 4 * 1.分享到微信朋友圈 5 * 2.分享给微信好友 6 * 3.分享到腾讯微博 7 * 4.新的分享接口, ...
- Arcgis for Javascript API下类似于百度搜索A、B、C、D marker的实现方式
原文:Arcgis for Javascript API下类似于百度搜索A.B.C.D marker的实现方式 多说无益,首先贴两张图让大家看看具体的效果: 图1.百度地图搜索结果 图2.Arcgis ...
随机推荐
- c# 对html字符串进行编码
/// <summary> /// 对html字符串进行编码 /// </summary> /// <param name="html">htm ...
- LINQ 小项目【组合查询、分页】
使用 linq 在网页上对用户信息增删改,组合查询,分页显示 using System; using System.Collections.Generic; using System.Linq; us ...
- spark基本概念及入门
spark spark背景 什么是spark Spark是一种快速.通用.可扩展的大数据分析引擎,2009年诞生于加州大学伯克利分校AMPLab,2010年开源,2013年6月成为Apache孵化项目 ...
- Archlinux/Manjaro使用笔记-使用makepkg安装软件 报错:未找到strip分割所需的二进制文件 的解决方法
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 使用archlinux或manjaro安装aurman时遇到如下报错 错误:未找到strip分割所需的二进制文件 原因:未安装g ...
- JavaScript 频繁发射事件处理的优化 --- 函数节流/事件稀释
引子:昨天面试时面试官问了如何实现一个固定导航栏,在我答完后面试官问我可能存在哪些问题,如何优化? 这个问题我答得不太好,但现在回想起来应该有两个问题: 1. 把 fixbar元素 position: ...
- python之while循环/格式化输出/运算符/初始编码/成员变量
一.主要内容:1.while 循环 (难点)while 条件: 循环体 break: 直接跳出循环continue:停止当前本次循环,继续执行下一次循环.不会中断循环能让循环退出:(1)break ( ...
- 安卓开发_关于WebView使用链接时调用浏览器显示的问题
在我们的实际开发中,我们用到WebView就是为了在自己的APP中的某个部分来显示指定网页的效果. 但是在学习的过程中,我发现一个问题: 有的网页使用WebView控件显示出来以后,再点击网页中的某个 ...
- 安卓开发_WebView如何在Fragment中使用
之前学习了如何在activity中使用WebView控件来显示网页. 在我的实际开发中,有需要在Fragment中用到WebView控件的,那么就百度学习了一下 其实很简单,但是当然不是和在Activ ...
- 安卓开发_关于WebView加载页面空白问题
依据我自己的测试,发现有时候用APP打开网页的时候,有的网页加载成功之前需要很久,有的一下就出来了(比如百度) 当加载时间过长的情况下,这时候显示的是空白界面,其实不是代码问题,只是要打开的这个网页太 ...
- 微信小程序开发--路由切换,页面重定向
这段时间开发了一个微信小程序,虽然小程序的导航API 官方文档写得很详细,但是在具体开发过程中还是会遇到很多不明白,或者一时转不过弯的地方. 1.页面切换传参,参数读取 1.1 wx.navigat ...