@eternl/cardano-dapp-connector-bridge
Version:
A postMessage bridge to connect dApps to the Eternl DApp Browser loading apps into an iframe.
218 lines (216 loc) • 9.31 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCardanoDAppConnectorBridge = void 0;
const generateUID = () => {
return ('000' + ((Math.random() * 46656) | 0).toString(36)).slice(-3) +
('000' + ((Math.random() * 46656) | 0).toString(36)).slice(-3);
};
/**
* Initialize the Cardano DApp Connector Bridge to the Eternl wallet DApp Browser.
*
* @param onBridgeCreated (optional) callback function to be called when the bridge is established.
*/
const initCardanoDAppConnectorBridge = (onBridgeCreated) => {
if (typeof window === 'undefined') {
return;
}
const _debug = false; // set to true for debug logs.
const _label = 'DAppConnectorBridge: '; // set to true for debug logs.
let _walletNamespace = null; // eg. 'eternl'
let _initialApiObject = null; // CIP0030 initial api object
let _fullApiObject = null; // CIP0030 full api object
const _bridge = { type: 'cardano-dapp-connector-bridge', source: null, origin: null };
const _requestMap = {};
const _methodMap = {
// Initial 4 methods to establish connection. More endpoints will be added by the wallet.
connect: 'connect',
handshake: 'handshake',
enable: 'enable',
isEnabled: 'isEnabled',
supportedExtensions: 'supportedExtensions'
};
function createRequest(method) {
const args = [...arguments];
if (args.length > 0) {
args.shift();
}
return new Promise(((resolve, reject) => {
var _a, _b;
const request = {
payload: {
type: _bridge.type,
to: _walletNamespace,
uid: generateUID(),
method: method,
args: args
},
resolve: resolve,
reject: reject
};
_requestMap[request.payload.uid] = request;
if (_debug) {
console.log(_label + '_requestMap:', _requestMap);
}
(_a = _bridge.source) === null || _a === void 0 ? void 0 : _a.postMessage(request.payload, (_b = _bridge.origin) !== null && _b !== void 0 ? _b : '*');
}));
}
function generateApiFunction(method) {
return function () {
// @ts-ignore
return createRequest(method, ...arguments);
};
}
function generateApiObject(obj) {
const apiObj = {};
for (const key in obj) {
const value = obj[key];
if (_debug) {
console.log(_label + 'init: key/value:', key, value);
}
if (typeof value === 'string') {
if (key === 'feeAddress') {
apiObj[key] = value;
}
else {
apiObj[key] = generateApiFunction(value);
_methodMap[value] = value;
}
}
else if (typeof value === 'object') {
apiObj[key] = generateApiObject(value);
}
else {
apiObj[key] = value;
}
}
return apiObj;
}
function initBridge(source, origin, walletNamespace, initialApi) {
var _a;
if (!window.hasOwnProperty('cardano')) {
window.cardano = {};
}
if (window.cardano.hasOwnProperty(walletNamespace)) {
console.warn('Warn: ' + _label + 'window.cardano.' + walletNamespace + ' already present, skipping initialApi creation.');
return null;
}
_bridge.source = source;
_bridge.origin = origin;
_walletNamespace = walletNamespace;
const initialApiObj = {
isBridge: true,
// https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030
isEnabled: function () { return createRequest('isEnabled'); },
enable: function () {
// @ts-ignore
return createRequest('enable', ...arguments);
},
apiVersion: initialApi.apiVersion,
name: initialApi.name,
icon: (_a = initialApi.icon) !== null && _a !== void 0 ? _a : null,
supportedExtensions: initialApi.supportedExtensions,
// experimental API: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0030/README.md#experimental-api
experimental: {}
};
window.cardano[walletNamespace] = initialApiObj;
if (initialApi.experimental) {
initialApiObj.experimental = Object.assign({}, generateApiObject(initialApi.experimental));
}
return window.cardano[walletNamespace];
}
function isValidBridge(event) {
if (!_initialApiObject) {
if (event.data.method !== _methodMap.connect) {
console.error('Error: ' + _label + 'send \'connect\' first.');
return false;
}
const initialApi = event.data.initialApi;
if (!initialApi || !initialApi.isBridge || !initialApi.apiVersion || !initialApi.name) {
console.error('Error: ' + _label + '\'connect\' is missing correct initialApi.', initialApi);
return false;
}
if (!event.data.walletNamespace) {
console.error('Error: ' + _label + '\'connect\' is missing walletNamespace.', event.data.walletNamespace);
return false;
}
_initialApiObject = initBridge(event.source, event.origin, event.data.walletNamespace, initialApi);
}
if (!(_initialApiObject && window.hasOwnProperty('cardano') && window.cardano[event.data.walletNamespace] === _initialApiObject)) {
console.warn('Warn: ' + _label + 'bridge not set up correctly:', _bridge, _initialApiObject, _walletNamespace);
return false;
}
return true;
}
function isValidMessage(event) {
if (!event.data || !event.origin || !event.source)
return false;
if (event.data.type !== _bridge.type)
return false;
if (!_methodMap.hasOwnProperty(event.data.method))
return false;
if (_walletNamespace && event.data.walletNamespace !== _walletNamespace)
return false;
return true;
}
function onMessage(event) {
return __awaiter(this, void 0, void 0, function* () {
if (!isValidMessage(event) ||
!isValidBridge(event)) {
return;
}
if (_debug) {
console.log('########################');
console.log(_label + 'onMessage: got message');
console.log(_label + 'onMessage: origin:', event.origin);
// console.log(_label+'onMessage: source:', payload.source) // Don't log source, might break browser security rules
console.log(_label + 'onMessage: data: ', event.data);
console.log('########################');
}
if (event.data.method === _methodMap.connect) {
const success = yield createRequest('handshake');
if (success && _initialApiObject) {
if (onBridgeCreated)
onBridgeCreated(_initialApiObject);
}
return;
}
if (!event.data.uid) {
return;
}
const request = _requestMap[event.data.uid];
if (!request)
return;
const error = event.data.error;
if (error) {
request.reject(error);
delete _requestMap[event.data.uid];
return;
}
// Bridge is set up correctly, message is valid, method is known.
let response = event.data.response;
if (event.data.method === _methodMap.enable) {
_fullApiObject = null;
if (typeof response === 'object') {
_fullApiObject = Object.assign({}, generateApiObject(response));
response = _fullApiObject;
if (_debug) {
console.log(_label + 'onMessage: fullApiObject:', _fullApiObject);
}
}
}
request.resolve(response);
delete _requestMap[event.data.uid];
});
}
window.addEventListener("message", onMessage, false);
};
exports.initCardanoDAppConnectorBridge = initCardanoDAppConnectorBridge;