Skip to main content

evm

OpenBlock loads Dapp via an iframe, and the Dapp interacts with the OpenBlock wallet using the standard web3 rpc methods

tip
  1. use window.obethereum replace window.ethereum

  2. OpenBlock will send a message to the DApp when the Dapp is loaded. The Dapp can use this message to determine if the host application is OpenBlock and then load innerdappsdk:

       // openblock side:
    dappPageIframe.onload = () => {
    dappPageIframe.contentWindow?.postMessage(
    {
    from: "openblock-content-integration",
    target: 'dapp-inpage-integration',
    value: 'dapploaded',
    },
    "*"
    );
    };
       // dapp side:
    window.addEventListener(
    'message',
    event => {
    if (event.data.target === 'dapp-inpage-integration' && event.data.from === 'openblock-content-integration') {
    // fom openblock wallet
    return;
    }
    },
    false
    );

Source Code

inner-dappsdk.js

const DAPP_INPAGE = 'dapp-inpage-integration';
const OPENBLOCK_CONTENT = 'openblock-content-integration';
const REQUEST_AUTH = 'eth_requestAccounts';
const EVENT_NEED_RELOAD_LIST = [
'https://cocoswap.com',
'https://app.openocean.finance',
];
const IGNORE_EVENT_LIST = ['https://oec.cocoswap.com'];

const guid = () => {
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
};
class openBlockInpageProvider {
constructor() {
console.log('start initialize openblockinpage provider');
this.callBackList = {};
this.eventListener = {};
this.isMetaMask = true;
this.isOpenBlock = true;
this.chainId = null;
this.networkVersion = null;
this.selectedAddress = null;
this.request = this.request.bind(this);
this.send = this.send.bind(this);
this.sendAsync = this.sendAsync.bind(this);
this.on = this.on.bind(this);
this.isConnected = this.isConnected.bind(this);
this.enable = this.enable.bind(this);
this._metamask = this._getExperimentalApi();
this._initInpageListener();
this.getProviderData();
}

async getProviderData() {
const res = await this.request('metamask_getProviderState');
this.chainId = res?.chainId;
this.networkVersion = res?.networkVersion;
this.selectedAddress = res?.accounts?.[0];
}

async request(params) {
if (params.method === REQUEST_AUTH) {
const res = await this._hookRequest('eth_accounts');
if (res.length) {
return new Promise(resolve => resolve(res));
}
}
return this._hookRequest(params);
}

async send(...params) {
if (typeof params[1] === 'function') {
this.sendAsync(params[0], params[1]);
return;
}
if (typeof params[0] === 'string') {
return this.request({ method: params[0], params: params[1] });
}
if (params.length === 1) {
return this.request(params);
}
}
async sendAsync(...paramsArr) {
const paramsObj = {
method: paramsArr[0].method,
params: paramsArr[0].params,
};
const request_id = paramsArr[0].id;
this.request(paramsObj)
.then(res => {
paramsArr[1](null, { id: request_id, jsonrpc: '2.0', result: res });
})
.catch(error => {
paramsArr[1](null, { id: request_id, jsonrpc: '2.0', error });
});
}
isConnected() {
return true;
}

async enable() {
const res = await this._hookRequest('eth_accounts');
if (res.length) {
return new Promise(resolve => resolve(res));
}
return this.request({ method: 'eth_requestAccounts' });
}

on(event, callback) {
if (typeof event !== 'string' || typeof callback !== 'function') {
return;
}
if (this.eventListener[event] && this.eventListener[event].length > 5) {
return;
} else if (this.eventListener[event]) {
this.eventListener[event].push(callback);
this.eventListener[event] = Array.from(
new Set(this.eventListener[event])
);
} else if (!this.eventListener[event]) {
this.eventListener = {
...this.eventListener,
[event]: [callback],
};
}
////console.log("this.eventListener", this.eventListener);
this._registerEvent(event);
}
removeListener() {}
_initInpageListener() {
window.addEventListener(
'message',
event => {
if (event.data.target !== DAPP_INPAGE) {
return;
}
let mark = event.data?.value?.data?.mark;

if (mark) {
if (mark.type === 'call_method') {
this._handleNormalMethod(mark, event);
} else if (mark.type === 'register_event') {
this._handleEventMethod(mark, event);
}
}
},
false
);
}
_handleNormalMethod(mark, event) {
if (event.data.value.data?.error) {
this.callBackList[mark.id]?.['reject'](event.data.value.data?.error);
} else {
this.callBackList[mark.id]?.['resolve'](event.data.value.data?.result);
}
delete this.callBackList[mark.id];
}
_handleEventMethod(mark, event) {
let eventName = event.data?.value?.data?.mark?.eventName;
let params = event.data?.value?.data?.params;
if (eventName) {
const fireList = this.eventListener[eventName];
//console.log('fireList', fireList)
if (!fireList) {
if (window.location.origin === 'https://app.openocean.finance') {
window.location.reload();
}
return;
}
fireList.forEach(func => {
if (eventName === 'chainChanged') {
this.chainId = params;
this.networkVersion = Number(params);
}
if (
IGNORE_EVENT_LIST.includes(window.location.origin) &&
eventName === 'chainChanged'
)
return;
if (EVENT_NEED_RELOAD_LIST.includes(window.location.origin)) {
window.location.reload();
} else {
func(params);
}
});
}
}

_hookRequest(paramsObj) {
return new Promise((resolve, reject) => {
const _guid = guid();

if (typeof paramsObj === 'string') {
paramsObj = {
method: paramsObj,
};
}
paramsObj = {
...paramsObj,
mark: {
id: _guid,
method: paramsObj.method,
type: 'call_method',
},
};

window.parent.postMessage(
{
from: DAPP_INPAGE,
target: OPENBLOCK_CONTENT,
data: paramsObj,
},
'*'
);

this.callBackList[_guid] = {
resolve,
reject,
};
});
}
_registerEvent(event) {
let value = {
mark: {
eventName: event,
type: 'register_event',
},
eventName: event,
};
//console.log('0.registerEvent', event, value)

window.parent.postMessage(
{ from: DAPP_INPAGE, target: OPENBLOCK_CONTENT, data: value },
'*'
);
}

_getExperimentalApi() {
return new Proxy(
{
isUnlocked: () => new Promise(resolve => resolve(true)),
},
{
get: (obj, prop, ...args) => {
return Reflect.get(obj, prop, ...args);
},
}
);
}
}

function initializeProvider() {
if (window.obethereum?.isOpenBlock) {
return;
}
let provider = new openBlockInpageProvider();
provider = new Proxy(provider, {
deleteProperty: () => true,
});

try {
delete window.obethereum;
Object.defineProperty(window, 'obethereum', {
set: function (newEthereum) {
console.log('set ethereum', newEthereum);
},
get: function () {
return provider;
},
});
} catch (error) {
console.log('initializeProvider error', error);
window.obethereum = provider;
}
window.web3 = { ...window.web3 };
window.web3.currentProvider = provider;
window.dispatchEvent(new Event('ethereum#initialized'));
}

if (typeof window !== 'undefined') {
initializeProvider();
}

Ethereum

import './inner-dappsdk';

window.addEventListener('DOMContentLoaded', () => {
const provider = window.obethereum;

// Config
provider.autoRefreshOnNetworkChange = false;

// On Chain Changed
provider.on('chainChanged', (chain) => {
// handler
});

// On Accounts Changed
provider.on('accountsChanged', (accounts) => {
// handler
});

// Request Accounts
const accounts = await provider.request({
method: 'eth_requestAccounts',
});

// Chain ID
const chainId = await provider.request({
method: 'eth_chainId',
});

// Net Version
const networkId = await provider.request({
method: 'net_version',
});

// Get Block By Number
const block = await provider.request({
method: 'eth_getBlockByNumber'
});

// Switch Ethereum Chain
await provider.request({
method: 'wallet_switchEthereumChain',
params: [
{
chainId,
}
]
});

// Send Transaction
await provider.request({
method: 'eth_sendTransaction',
params: [
{
from,
to,
value,
gasLimit,
gasPrice,
type,
data,
}
]
});

// Add Ethereum Chain
await provider.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId,
rpcUrls: [
'https://http-mainner-node.huobichain.com',
'https://http-mainner.hecochain.com',
],
chainName: 'Huobi ECO Chain',
nativeCurrency: { name: 'HECO', decimals: '18', symbol: 'HT' },
blockExplorerUrls: ['https://hecoinfo.com'],
}
]
});

// Sign
await provider.request({
method: 'eth_sign',
params: ['accounts', 'msg'],
});

// Personal Sign
await provider.request({
method: 'personal_sign',
params: ['msg', 'from'],
});

// eth_signTypedData_v4
await provider.request({
method: 'eth_signTypedData_v4',
params: ['accounts','typedata msg'],
});

});