@reown/appkit-coinbase-react-native 0.0.0-feat-coinbase-20250722173213
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/connectors/CoinbaseConnector.js +133 -0
- package/lib/commonjs/connectors/CoinbaseConnector.js.map +1 -0
- package/lib/commonjs/index.js +13 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/providers/CoinbaseProvider.js +70 -0
- package/lib/commonjs/providers/CoinbaseProvider.js.map +1 -0
- package/lib/commonjs/types.js +6 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/utils.js +59 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/connectors/CoinbaseConnector.js +126 -0
- package/lib/module/connectors/CoinbaseConnector.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/providers/CoinbaseProvider.js +62 -0
- package/lib/module/providers/CoinbaseProvider.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils.js +49 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/connectors/CoinbaseConnector.d.ts +22 -0
- package/lib/typescript/connectors/CoinbaseConnector.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +3 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/providers/CoinbaseProvider.d.ts +16 -0
- package/lib/typescript/providers/CoinbaseProvider.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +17 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/utils.d.ts +18 -0
- package/lib/typescript/utils.d.ts.map +1 -0
- package/package.json +62 -0
- package/src/connectors/CoinbaseConnector.ts +160 -0
- package/src/index.ts +2 -0
- package/src/providers/CoinbaseProvider.ts +75 -0
- package/src/types.ts +27 -0
- package/src/utils.ts +68 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CoinbaseConnector = void 0;
|
|
7
|
+
var _appkitCommonReactNative = require("@reown/appkit-common-react-native");
|
|
8
|
+
var _utils = require("../utils");
|
|
9
|
+
var _CoinbaseProvider = require("../providers/CoinbaseProvider");
|
|
10
|
+
const SESSION_KEY = '@appkit/coinbase-connector/session';
|
|
11
|
+
class CoinbaseConnector extends _appkitCommonReactNative.WalletConnector {
|
|
12
|
+
static SUPPORTED_NAMESPACE = 'eip155';
|
|
13
|
+
constructor(config) {
|
|
14
|
+
super({
|
|
15
|
+
type: 'coinbase'
|
|
16
|
+
});
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
async init(ops) {
|
|
20
|
+
super.init(ops);
|
|
21
|
+
if (!ops.metadata.redirect?.universal) {
|
|
22
|
+
throw new Error('CoinbaseConnector: Universal link not found in metadata');
|
|
23
|
+
}
|
|
24
|
+
this.provider = new _CoinbaseProvider.CoinbaseProvider({
|
|
25
|
+
redirect: ops.metadata.redirect.universal,
|
|
26
|
+
// use config storage only, as it needs to be mmkv-compatible
|
|
27
|
+
storage: this.config.storage
|
|
28
|
+
});
|
|
29
|
+
await this.restoreSession();
|
|
30
|
+
}
|
|
31
|
+
async connect(opts) {
|
|
32
|
+
const accounts = await this.getProvider().connect();
|
|
33
|
+
const namespaces = (0, _utils.getCoinbaseNamespace)(opts?.namespaces, accounts);
|
|
34
|
+
this.namespaces = namespaces;
|
|
35
|
+
this.saveSession(namespaces);
|
|
36
|
+
return this.namespaces;
|
|
37
|
+
}
|
|
38
|
+
async disconnect() {
|
|
39
|
+
await super.disconnect();
|
|
40
|
+
this.deleteSession();
|
|
41
|
+
}
|
|
42
|
+
getProvider() {
|
|
43
|
+
if (!this.provider) {
|
|
44
|
+
throw new Error('CoinbaseConnector: Provider not initialized');
|
|
45
|
+
}
|
|
46
|
+
return this.provider;
|
|
47
|
+
}
|
|
48
|
+
getNamespaces() {
|
|
49
|
+
if (!this.namespaces) {
|
|
50
|
+
throw new Error('CoinbaseConnector: Namespaces not initialized');
|
|
51
|
+
}
|
|
52
|
+
return this.namespaces;
|
|
53
|
+
}
|
|
54
|
+
getChainId() {
|
|
55
|
+
const hexChainId = this.getProvider().getChainId();
|
|
56
|
+
const chainId = (0, _utils.hexToString)(hexChainId);
|
|
57
|
+
return `${CoinbaseConnector.SUPPORTED_NAMESPACE}:${chainId}`;
|
|
58
|
+
}
|
|
59
|
+
getWalletInfo() {
|
|
60
|
+
// TODO: Add icon
|
|
61
|
+
return {
|
|
62
|
+
name: 'Coinbase Wallet',
|
|
63
|
+
description: 'Your home to everything onchain',
|
|
64
|
+
url: 'https://www.coinbase.com/wallet'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
getProperties() {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
async switchNetwork(network) {
|
|
71
|
+
const provider = this.getProvider();
|
|
72
|
+
const chainId_ = (0, _utils.numberToHex)(network.id);
|
|
73
|
+
try {
|
|
74
|
+
await provider.request({
|
|
75
|
+
method: 'wallet_switchEthereumChain',
|
|
76
|
+
params: [{
|
|
77
|
+
chainId: chainId_
|
|
78
|
+
}]
|
|
79
|
+
});
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Indicates chain is not added to provider
|
|
82
|
+
if (error?.code === 4902) {
|
|
83
|
+
try {
|
|
84
|
+
await provider.request({
|
|
85
|
+
method: 'wallet_addEthereumChain',
|
|
86
|
+
params: [{
|
|
87
|
+
chainId: chainId_,
|
|
88
|
+
chainName: network.name,
|
|
89
|
+
nativeCurrency: network.nativeCurrency,
|
|
90
|
+
rpcUrls: [network.rpcUrls.default?.http[0] ?? ''],
|
|
91
|
+
blockExplorerUrls: [network.blockExplorers?.default.url]
|
|
92
|
+
}]
|
|
93
|
+
});
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.warn('CoinbaseConnector: switchNetwork error', e);
|
|
96
|
+
throw e;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.warn('CoinbaseConnector: switchNetwork error', error);
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
deleteSession() {
|
|
104
|
+
const storage = this.getStorage();
|
|
105
|
+
storage.removeItem(SESSION_KEY);
|
|
106
|
+
}
|
|
107
|
+
saveSession(namespaces) {
|
|
108
|
+
const storage = this.getStorage();
|
|
109
|
+
storage.setItem(SESSION_KEY, JSON.stringify({
|
|
110
|
+
namespaces
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
async restoreSession() {
|
|
114
|
+
const storage = this.getStorage();
|
|
115
|
+
const session = await storage.getItem(SESSION_KEY);
|
|
116
|
+
if (!session) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const {
|
|
120
|
+
namespaces
|
|
121
|
+
} = JSON.parse(session);
|
|
122
|
+
this.namespaces = namespaces;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
getStorage() {
|
|
126
|
+
if (!this.storage) {
|
|
127
|
+
throw new Error('CoinbaseConnector: Storage not initialized');
|
|
128
|
+
}
|
|
129
|
+
return this.storage;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
exports.CoinbaseConnector = CoinbaseConnector;
|
|
133
|
+
//# sourceMappingURL=CoinbaseConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_appkitCommonReactNative","require","_utils","_CoinbaseProvider","SESSION_KEY","CoinbaseConnector","WalletConnector","SUPPORTED_NAMESPACE","constructor","config","type","init","ops","metadata","redirect","universal","Error","provider","CoinbaseProvider","storage","restoreSession","connect","opts","accounts","getProvider","namespaces","getCoinbaseNamespace","saveSession","disconnect","deleteSession","getNamespaces","getChainId","hexChainId","chainId","hexToString","getWalletInfo","name","description","url","getProperties","undefined","switchNetwork","network","chainId_","numberToHex","id","request","method","params","error","code","chainName","nativeCurrency","rpcUrls","default","http","blockExplorerUrls","blockExplorers","e","console","warn","getStorage","removeItem","setItem","JSON","stringify","session","getItem","parse","exports"],"sourceRoot":"../../../src","sources":["connectors/CoinbaseConnector.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "CoinbaseConnector", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _CoinbaseConnector.CoinbaseConnector;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _CoinbaseConnector = require("./connectors/CoinbaseConnector");
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_CoinbaseConnector","require"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;AAAA,IAAAA,kBAAA,GAAAC,OAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CoinbaseProvider = void 0;
|
|
7
|
+
var _events = _interopRequireDefault(require("events"));
|
|
8
|
+
var _walletMobileSdk = require("@coinbase/wallet-mobile-sdk");
|
|
9
|
+
var _utils = require("../utils");
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
class CoinbaseProvider extends _events.default {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = config;
|
|
15
|
+
(0, _walletMobileSdk.configure)({
|
|
16
|
+
hostURL: new URL('https://wallet.coinbase.com/wsegue'),
|
|
17
|
+
callbackURL: new URL(this.config.redirect),
|
|
18
|
+
// App Universal Link
|
|
19
|
+
hostPackageName: 'org.toshi'
|
|
20
|
+
});
|
|
21
|
+
this.provider = new _walletMobileSdk.WalletMobileSDKEVMProvider({
|
|
22
|
+
...this.config,
|
|
23
|
+
jsonRpcUrl: this.config.rpcUrl,
|
|
24
|
+
chainId: this.config.defaultChain,
|
|
25
|
+
storage: this.config.storage
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async connect() {
|
|
29
|
+
try {
|
|
30
|
+
let accounts = [];
|
|
31
|
+
const isConnected = this.provider.connected;
|
|
32
|
+
if (!isConnected) {
|
|
33
|
+
accounts = await this.provider.request({
|
|
34
|
+
method: 'eth_requestAccounts',
|
|
35
|
+
params: []
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
accounts = this.provider.selectedAddress ? [this.provider.selectedAddress] : [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//TODO: check switch chain
|
|
42
|
+
|
|
43
|
+
return accounts;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn('CoinbaseProvider: connect error', error);
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async disconnect() {
|
|
50
|
+
this.provider.disconnect();
|
|
51
|
+
return Promise.resolve();
|
|
52
|
+
}
|
|
53
|
+
request(args) {
|
|
54
|
+
if (!(0, _utils.isValidMethod)(args.method)) {
|
|
55
|
+
throw new Error(`CoinbaseProvider: Invalid method: ${args.method}`);
|
|
56
|
+
}
|
|
57
|
+
return this.provider.request(args);
|
|
58
|
+
}
|
|
59
|
+
getChainId() {
|
|
60
|
+
return this.provider.chainId;
|
|
61
|
+
}
|
|
62
|
+
on(event, listener) {
|
|
63
|
+
return this.provider.on(event, listener);
|
|
64
|
+
}
|
|
65
|
+
off(event, listener) {
|
|
66
|
+
return this.provider.off(event, listener);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.CoinbaseProvider = CoinbaseProvider;
|
|
70
|
+
//# sourceMappingURL=CoinbaseProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_events","_interopRequireDefault","require","_walletMobileSdk","_utils","e","__esModule","default","CoinbaseProvider","EventEmitter","constructor","config","configure","hostURL","URL","callbackURL","redirect","hostPackageName","provider","WalletMobileSDKEVMProvider","jsonRpcUrl","rpcUrl","chainId","defaultChain","storage","connect","accounts","isConnected","connected","request","method","params","selectedAddress","error","console","warn","disconnect","Promise","resolve","args","isValidMethod","Error","getChainId","on","event","listener","off","exports"],"sourceRoot":"../../../src","sources":["providers/CoinbaseProvider.ts"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,sBAAA,CAAAC,OAAA;AAEA,IAAAC,gBAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAF,OAAA;AAAyC,SAAAD,uBAAAI,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAGlC,MAAMG,gBAAgB,SAASC,eAAY,CAAqB;EAIrEC,WAAWA,CAACC,MAA8B,EAAE;IAC1C,KAAK,CAAC,CAAC;IACP,IAAI,CAACA,MAAM,GAAGA,MAAM;IACpB,IAAAC,0BAAS,EAAC;MACRC,OAAO,EAAE,IAAIC,GAAG,CAAC,oCAAoC,CAAC;MACtDC,WAAW,EAAE,IAAID,GAAG,CAAC,IAAI,CAACH,MAAM,CAACK,QAAQ,CAAC;MAAE;MAC5CC,eAAe,EAAE;IACnB,CAAC,CAAC;IACF,IAAI,CAACC,QAAQ,GAAG,IAAIC,2CAA0B,CAAC;MAC7C,GAAG,IAAI,CAACR,MAAM;MACdS,UAAU,EAAE,IAAI,CAACT,MAAM,CAACU,MAAM;MAC9BC,OAAO,EAAE,IAAI,CAACX,MAAM,CAACY,YAAY;MACjCC,OAAO,EAAE,IAAI,CAACb,MAAM,CAACa;IACvB,CAAC,CAAC;EACJ;EAEA,MAAMC,OAAOA,CAAA,EAA6B;IACxC,IAAI;MACF,IAAIC,QAAkB,GAAG,EAAE;MAC3B,MAAMC,WAAW,GAAG,IAAI,CAACT,QAAQ,CAACU,SAAS;MAE3C,IAAI,CAACD,WAAW,EAAE;QAChBD,QAAQ,GAAG,MAAM,IAAI,CAACR,QAAQ,CAACW,OAAO,CAAC;UACrCC,MAAM,EAAE,qBAAqB;UAC7BC,MAAM,EAAE;QACV,CAAC,CAAC;MACJ,CAAC,MAAM;QACLL,QAAQ,GAAG,IAAI,CAACR,QAAQ,CAACc,eAAe,GAAG,CAAC,IAAI,CAACd,QAAQ,CAACc,eAAe,CAAC,GAAG,EAAE;MACjF;;MAEA;;MAEA,OAAON,QAAQ;IACjB,CAAC,CAAC,OAAOO,KAAK,EAAE;MACdC,OAAO,CAACC,IAAI,CAAC,iCAAiC,EAAEF,KAAK,CAAC;MAEtD,MAAMA,KAAK;IACb;EACF;EACA,MAAMG,UAAUA,CAAA,EAAkB;IAChC,IAAI,CAAClB,QAAQ,CAACkB,UAAU,CAAC,CAAC;IAE1B,OAAOC,OAAO,CAACC,OAAO,CAAC,CAAC;EAC1B;EAEAT,OAAOA,CAAcU,IAAsB,EAAc;IACvD,IAAI,CAAC,IAAAC,oBAAa,EAACD,IAAI,CAACT,MAAM,CAAC,EAAE;MAC/B,MAAM,IAAIW,KAAK,CAAE,qCAAoCF,IAAI,CAACT,MAAO,EAAC,CAAC;IACrE;IAEA,OAAO,IAAI,CAACZ,QAAQ,CAACW,OAAO,CAACU,IAAI,CAAC;EACpC;EAEAG,UAAUA,CAAA,EAAkB;IAC1B,OAAO,IAAI,CAACxB,QAAQ,CAACI,OAAO;EAC9B;EAESqB,EAAEA,CAACC,KAAa,EAAEC,QAA8B,EAAO;IAC9D,OAAO,IAAI,CAAC3B,QAAQ,CAACyB,EAAE,CAACC,KAAK,EAAEC,QAAQ,CAAC;EAC1C;EAESC,GAAGA,CAACF,KAAa,EAAEC,QAA8B,EAAO;IAC/D,OAAO,IAAI,CAAC3B,QAAQ,CAAC4B,GAAG,CAACF,KAAK,EAAEC,QAAQ,CAAC;EAC3C;AACF;AAACE,OAAA,CAAAvC,gBAAA,GAAAA,gBAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.COINBASE_METHODS = void 0;
|
|
7
|
+
exports.getCoinbaseNamespace = getCoinbaseNamespace;
|
|
8
|
+
exports.hexToString = hexToString;
|
|
9
|
+
exports.isValidMethod = isValidMethod;
|
|
10
|
+
exports.numberToHex = numberToHex;
|
|
11
|
+
const COINBASE_METHODS = exports.COINBASE_METHODS = {
|
|
12
|
+
REQUEST_ACCOUNTS: 'eth_requestAccounts',
|
|
13
|
+
SIGN_TRANSACTION: 'eth_signTransaction',
|
|
14
|
+
SEND_TRANSACTION: 'eth_sendTransaction',
|
|
15
|
+
SIGN_MESSAGE: 'personal_sign',
|
|
16
|
+
SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3',
|
|
17
|
+
SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4',
|
|
18
|
+
SWITCH_CHAIN: 'wallet_switchEthereumChain',
|
|
19
|
+
ADD_ETHEREUM_CHAIN: 'wallet_addEthereumChain',
|
|
20
|
+
WATCH_ASSET: 'wallet_watchAsset'
|
|
21
|
+
};
|
|
22
|
+
function isValidMethod(method) {
|
|
23
|
+
return Object.values(COINBASE_METHODS).includes(method);
|
|
24
|
+
}
|
|
25
|
+
function getCoinbaseNamespace(namespaces, accounts) {
|
|
26
|
+
if (!namespaces || !accounts) {
|
|
27
|
+
throw new Error('CoinbaseConnector: Namespaces or accounts not found');
|
|
28
|
+
}
|
|
29
|
+
const namespace = namespaces['eip155'];
|
|
30
|
+
if (!namespace) {
|
|
31
|
+
throw new Error('CoinbaseConnector: Namespace not found');
|
|
32
|
+
}
|
|
33
|
+
let caipAddresses = [];
|
|
34
|
+
for (const account of accounts) {
|
|
35
|
+
namespace.chains?.forEach(chain => {
|
|
36
|
+
caipAddresses.push(`${chain}:${account}`);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
['eip155']: {
|
|
41
|
+
...namespace,
|
|
42
|
+
methods: Object.values(COINBASE_METHODS),
|
|
43
|
+
accounts: caipAddresses
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function numberToHex(value) {
|
|
48
|
+
// Convert string to number first, then to hex
|
|
49
|
+
const num = typeof value === 'string' ? parseInt(value, 10) : value;
|
|
50
|
+
return `0x${num.toString(16)}`;
|
|
51
|
+
}
|
|
52
|
+
function hexToString(hexValue) {
|
|
53
|
+
// Remove 0x prefix if present
|
|
54
|
+
const cleanHex = hexValue.startsWith('0x') ? hexValue.slice(2) : hexValue;
|
|
55
|
+
// Convert hex to decimal number, then to string
|
|
56
|
+
|
|
57
|
+
return parseInt(cleanHex, 16).toString();
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["COINBASE_METHODS","exports","REQUEST_ACCOUNTS","SIGN_TRANSACTION","SEND_TRANSACTION","SIGN_MESSAGE","SIGN_TYPED_DATA_V3","SIGN_TYPED_DATA_V4","SWITCH_CHAIN","ADD_ETHEREUM_CHAIN","WATCH_ASSET","isValidMethod","method","Object","values","includes","getCoinbaseNamespace","namespaces","accounts","Error","namespace","caipAddresses","account","chains","forEach","chain","push","methods","numberToHex","value","num","parseInt","toString","hexToString","hexValue","cleanHex","startsWith","slice"],"sourceRoot":"../../src","sources":["utils.ts"],"mappings":";;;;;;;;;;AAOO,MAAMA,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG;EAC9BE,gBAAgB,EAAE,qBAAqB;EACvCC,gBAAgB,EAAE,qBAAqB;EACvCC,gBAAgB,EAAE,qBAAqB;EACvCC,YAAY,EAAE,eAAe;EAC7BC,kBAAkB,EAAE,sBAAsB;EAC1CC,kBAAkB,EAAE,sBAAsB;EAC1CC,YAAY,EAAE,4BAA4B;EAC1CC,kBAAkB,EAAE,yBAAyB;EAC7CC,WAAW,EAAE;AACf,CAAU;AAEH,SAASC,aAAaA,CAACC,MAAc,EAA4B;EACtE,OAAOC,MAAM,CAACC,MAAM,CAACd,gBAAgB,CAAC,CAACe,QAAQ,CAACH,MAAwB,CAAC;AAC3E;AAEO,SAASI,oBAAoBA,CAClCC,UAA+B,EAC/BC,QAAmB,EACP;EACZ,IAAI,CAACD,UAAU,IAAI,CAACC,QAAQ,EAAE;IAC5B,MAAM,IAAIC,KAAK,CAAC,qDAAqD,CAAC;EACxE;EAEA,MAAMC,SAAS,GAAGH,UAAU,CAAC,QAAQ,CAAC;EAEtC,IAAI,CAACG,SAAS,EAAE;IACd,MAAM,IAAID,KAAK,CAAC,wCAAwC,CAAC;EAC3D;EAEA,IAAIE,aAA4B,GAAG,EAAE;EAErC,KAAK,MAAMC,OAAO,IAAIJ,QAAQ,EAAE;IAC9BE,SAAS,CAACG,MAAM,EAAEC,OAAO,CAACC,KAAK,IAAI;MACjCJ,aAAa,CAACK,IAAI,CAAE,GAAED,KAAM,IAAGH,OAAQ,EAAC,CAAC;IAC3C,CAAC,CAAC;EACJ;EAEA,OAAO;IACL,CAAC,QAAQ,GAAG;MACV,GAAGF,SAAS;MACZO,OAAO,EAAEd,MAAM,CAACC,MAAM,CAACd,gBAAgB,CAAC;MACxCkB,QAAQ,EAAEG;IACZ;EACF,CAAC;AACH;AAEO,SAASO,WAAWA,CAACC,KAAsB,EAAE;EAClD;EACA,MAAMC,GAAG,GAAG,OAAOD,KAAK,KAAK,QAAQ,GAAGE,QAAQ,CAACF,KAAK,EAAE,EAAE,CAAC,GAAGA,KAAK;EAEnE,OAAQ,KAAIC,GAAG,CAACE,QAAQ,CAAC,EAAE,CAAE,EAAC;AAChC;AAEO,SAASC,WAAWA,CAACC,QAAgB,EAAE;EAC5C;EACA,MAAMC,QAAQ,GAAGD,QAAQ,CAACE,UAAU,CAAC,IAAI,CAAC,GAAGF,QAAQ,CAACG,KAAK,CAAC,CAAC,CAAC,GAAGH,QAAQ;EACzE;;EAEA,OAAOH,QAAQ,CAACI,QAAQ,EAAE,EAAE,CAAC,CAACH,QAAQ,CAAC,CAAC;AAC1C"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { WalletConnector } from '@reown/appkit-common-react-native';
|
|
2
|
+
import { getCoinbaseNamespace, hexToString, numberToHex } from '../utils';
|
|
3
|
+
import { CoinbaseProvider } from '../providers/CoinbaseProvider';
|
|
4
|
+
const SESSION_KEY = '@appkit/coinbase-connector/session';
|
|
5
|
+
export class CoinbaseConnector extends WalletConnector {
|
|
6
|
+
static SUPPORTED_NAMESPACE = 'eip155';
|
|
7
|
+
constructor(config) {
|
|
8
|
+
super({
|
|
9
|
+
type: 'coinbase'
|
|
10
|
+
});
|
|
11
|
+
this.config = config;
|
|
12
|
+
}
|
|
13
|
+
async init(ops) {
|
|
14
|
+
super.init(ops);
|
|
15
|
+
if (!ops.metadata.redirect?.universal) {
|
|
16
|
+
throw new Error('CoinbaseConnector: Universal link not found in metadata');
|
|
17
|
+
}
|
|
18
|
+
this.provider = new CoinbaseProvider({
|
|
19
|
+
redirect: ops.metadata.redirect.universal,
|
|
20
|
+
// use config storage only, as it needs to be mmkv-compatible
|
|
21
|
+
storage: this.config.storage
|
|
22
|
+
});
|
|
23
|
+
await this.restoreSession();
|
|
24
|
+
}
|
|
25
|
+
async connect(opts) {
|
|
26
|
+
const accounts = await this.getProvider().connect();
|
|
27
|
+
const namespaces = getCoinbaseNamespace(opts?.namespaces, accounts);
|
|
28
|
+
this.namespaces = namespaces;
|
|
29
|
+
this.saveSession(namespaces);
|
|
30
|
+
return this.namespaces;
|
|
31
|
+
}
|
|
32
|
+
async disconnect() {
|
|
33
|
+
await super.disconnect();
|
|
34
|
+
this.deleteSession();
|
|
35
|
+
}
|
|
36
|
+
getProvider() {
|
|
37
|
+
if (!this.provider) {
|
|
38
|
+
throw new Error('CoinbaseConnector: Provider not initialized');
|
|
39
|
+
}
|
|
40
|
+
return this.provider;
|
|
41
|
+
}
|
|
42
|
+
getNamespaces() {
|
|
43
|
+
if (!this.namespaces) {
|
|
44
|
+
throw new Error('CoinbaseConnector: Namespaces not initialized');
|
|
45
|
+
}
|
|
46
|
+
return this.namespaces;
|
|
47
|
+
}
|
|
48
|
+
getChainId() {
|
|
49
|
+
const hexChainId = this.getProvider().getChainId();
|
|
50
|
+
const chainId = hexToString(hexChainId);
|
|
51
|
+
return `${CoinbaseConnector.SUPPORTED_NAMESPACE}:${chainId}`;
|
|
52
|
+
}
|
|
53
|
+
getWalletInfo() {
|
|
54
|
+
// TODO: Add icon
|
|
55
|
+
return {
|
|
56
|
+
name: 'Coinbase Wallet',
|
|
57
|
+
description: 'Your home to everything onchain',
|
|
58
|
+
url: 'https://www.coinbase.com/wallet'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
getProperties() {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
async switchNetwork(network) {
|
|
65
|
+
const provider = this.getProvider();
|
|
66
|
+
const chainId_ = numberToHex(network.id);
|
|
67
|
+
try {
|
|
68
|
+
await provider.request({
|
|
69
|
+
method: 'wallet_switchEthereumChain',
|
|
70
|
+
params: [{
|
|
71
|
+
chainId: chainId_
|
|
72
|
+
}]
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// Indicates chain is not added to provider
|
|
76
|
+
if (error?.code === 4902) {
|
|
77
|
+
try {
|
|
78
|
+
await provider.request({
|
|
79
|
+
method: 'wallet_addEthereumChain',
|
|
80
|
+
params: [{
|
|
81
|
+
chainId: chainId_,
|
|
82
|
+
chainName: network.name,
|
|
83
|
+
nativeCurrency: network.nativeCurrency,
|
|
84
|
+
rpcUrls: [network.rpcUrls.default?.http[0] ?? ''],
|
|
85
|
+
blockExplorerUrls: [network.blockExplorers?.default.url]
|
|
86
|
+
}]
|
|
87
|
+
});
|
|
88
|
+
} catch (e) {
|
|
89
|
+
console.warn('CoinbaseConnector: switchNetwork error', e);
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
console.warn('CoinbaseConnector: switchNetwork error', error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
deleteSession() {
|
|
98
|
+
const storage = this.getStorage();
|
|
99
|
+
storage.removeItem(SESSION_KEY);
|
|
100
|
+
}
|
|
101
|
+
saveSession(namespaces) {
|
|
102
|
+
const storage = this.getStorage();
|
|
103
|
+
storage.setItem(SESSION_KEY, JSON.stringify({
|
|
104
|
+
namespaces
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
async restoreSession() {
|
|
108
|
+
const storage = this.getStorage();
|
|
109
|
+
const session = await storage.getItem(SESSION_KEY);
|
|
110
|
+
if (!session) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const {
|
|
114
|
+
namespaces
|
|
115
|
+
} = JSON.parse(session);
|
|
116
|
+
this.namespaces = namespaces;
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
getStorage() {
|
|
120
|
+
if (!this.storage) {
|
|
121
|
+
throw new Error('CoinbaseConnector: Storage not initialized');
|
|
122
|
+
}
|
|
123
|
+
return this.storage;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=CoinbaseConnector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["WalletConnector","getCoinbaseNamespace","hexToString","numberToHex","CoinbaseProvider","SESSION_KEY","CoinbaseConnector","SUPPORTED_NAMESPACE","constructor","config","type","init","ops","metadata","redirect","universal","Error","provider","storage","restoreSession","connect","opts","accounts","getProvider","namespaces","saveSession","disconnect","deleteSession","getNamespaces","getChainId","hexChainId","chainId","getWalletInfo","name","description","url","getProperties","undefined","switchNetwork","network","chainId_","id","request","method","params","error","code","chainName","nativeCurrency","rpcUrls","default","http","blockExplorerUrls","blockExplorers","e","console","warn","getStorage","removeItem","setItem","JSON","stringify","session","getItem","parse"],"sourceRoot":"../../../src","sources":["connectors/CoinbaseConnector.ts"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["CoinbaseConnector"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,gCAAgC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import { configure, WalletMobileSDKEVMProvider } from '@coinbase/wallet-mobile-sdk';
|
|
3
|
+
import { isValidMethod } from '../utils';
|
|
4
|
+
export class CoinbaseProvider extends EventEmitter {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
super();
|
|
7
|
+
this.config = config;
|
|
8
|
+
configure({
|
|
9
|
+
hostURL: new URL('https://wallet.coinbase.com/wsegue'),
|
|
10
|
+
callbackURL: new URL(this.config.redirect),
|
|
11
|
+
// App Universal Link
|
|
12
|
+
hostPackageName: 'org.toshi'
|
|
13
|
+
});
|
|
14
|
+
this.provider = new WalletMobileSDKEVMProvider({
|
|
15
|
+
...this.config,
|
|
16
|
+
jsonRpcUrl: this.config.rpcUrl,
|
|
17
|
+
chainId: this.config.defaultChain,
|
|
18
|
+
storage: this.config.storage
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async connect() {
|
|
22
|
+
try {
|
|
23
|
+
let accounts = [];
|
|
24
|
+
const isConnected = this.provider.connected;
|
|
25
|
+
if (!isConnected) {
|
|
26
|
+
accounts = await this.provider.request({
|
|
27
|
+
method: 'eth_requestAccounts',
|
|
28
|
+
params: []
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
accounts = this.provider.selectedAddress ? [this.provider.selectedAddress] : [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//TODO: check switch chain
|
|
35
|
+
|
|
36
|
+
return accounts;
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.warn('CoinbaseProvider: connect error', error);
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async disconnect() {
|
|
43
|
+
this.provider.disconnect();
|
|
44
|
+
return Promise.resolve();
|
|
45
|
+
}
|
|
46
|
+
request(args) {
|
|
47
|
+
if (!isValidMethod(args.method)) {
|
|
48
|
+
throw new Error(`CoinbaseProvider: Invalid method: ${args.method}`);
|
|
49
|
+
}
|
|
50
|
+
return this.provider.request(args);
|
|
51
|
+
}
|
|
52
|
+
getChainId() {
|
|
53
|
+
return this.provider.chainId;
|
|
54
|
+
}
|
|
55
|
+
on(event, listener) {
|
|
56
|
+
return this.provider.on(event, listener);
|
|
57
|
+
}
|
|
58
|
+
off(event, listener) {
|
|
59
|
+
return this.provider.off(event, listener);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=CoinbaseProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["EventEmitter","configure","WalletMobileSDKEVMProvider","isValidMethod","CoinbaseProvider","constructor","config","hostURL","URL","callbackURL","redirect","hostPackageName","provider","jsonRpcUrl","rpcUrl","chainId","defaultChain","storage","connect","accounts","isConnected","connected","request","method","params","selectedAddress","error","console","warn","disconnect","Promise","resolve","args","Error","getChainId","on","event","listener","off"],"sourceRoot":"../../../src","sources":["providers/CoinbaseProvider.ts"],"mappings":"AAAA,OAAOA,YAAY,MAAM,QAAQ;AAEjC,SAASC,SAAS,EAAEC,0BAA0B,QAAQ,6BAA6B;AACnF,SAASC,aAAa,QAAQ,UAAU;AAGxC,OAAO,MAAMC,gBAAgB,SAASJ,YAAY,CAAqB;EAIrEK,WAAWA,CAACC,MAA8B,EAAE;IAC1C,KAAK,CAAC,CAAC;IACP,IAAI,CAACA,MAAM,GAAGA,MAAM;IACpBL,SAAS,CAAC;MACRM,OAAO,EAAE,IAAIC,GAAG,CAAC,oCAAoC,CAAC;MACtDC,WAAW,EAAE,IAAID,GAAG,CAAC,IAAI,CAACF,MAAM,CAACI,QAAQ,CAAC;MAAE;MAC5CC,eAAe,EAAE;IACnB,CAAC,CAAC;IACF,IAAI,CAACC,QAAQ,GAAG,IAAIV,0BAA0B,CAAC;MAC7C,GAAG,IAAI,CAACI,MAAM;MACdO,UAAU,EAAE,IAAI,CAACP,MAAM,CAACQ,MAAM;MAC9BC,OAAO,EAAE,IAAI,CAACT,MAAM,CAACU,YAAY;MACjCC,OAAO,EAAE,IAAI,CAACX,MAAM,CAACW;IACvB,CAAC,CAAC;EACJ;EAEA,MAAMC,OAAOA,CAAA,EAA6B;IACxC,IAAI;MACF,IAAIC,QAAkB,GAAG,EAAE;MAC3B,MAAMC,WAAW,GAAG,IAAI,CAACR,QAAQ,CAACS,SAAS;MAE3C,IAAI,CAACD,WAAW,EAAE;QAChBD,QAAQ,GAAG,MAAM,IAAI,CAACP,QAAQ,CAACU,OAAO,CAAC;UACrCC,MAAM,EAAE,qBAAqB;UAC7BC,MAAM,EAAE;QACV,CAAC,CAAC;MACJ,CAAC,MAAM;QACLL,QAAQ,GAAG,IAAI,CAACP,QAAQ,CAACa,eAAe,GAAG,CAAC,IAAI,CAACb,QAAQ,CAACa,eAAe,CAAC,GAAG,EAAE;MACjF;;MAEA;;MAEA,OAAON,QAAQ;IACjB,CAAC,CAAC,OAAOO,KAAK,EAAE;MACdC,OAAO,CAACC,IAAI,CAAC,iCAAiC,EAAEF,KAAK,CAAC;MAEtD,MAAMA,KAAK;IACb;EACF;EACA,MAAMG,UAAUA,CAAA,EAAkB;IAChC,IAAI,CAACjB,QAAQ,CAACiB,UAAU,CAAC,CAAC;IAE1B,OAAOC,OAAO,CAACC,OAAO,CAAC,CAAC;EAC1B;EAEAT,OAAOA,CAAcU,IAAsB,EAAc;IACvD,IAAI,CAAC7B,aAAa,CAAC6B,IAAI,CAACT,MAAM,CAAC,EAAE;MAC/B,MAAM,IAAIU,KAAK,CAAE,qCAAoCD,IAAI,CAACT,MAAO,EAAC,CAAC;IACrE;IAEA,OAAO,IAAI,CAACX,QAAQ,CAACU,OAAO,CAACU,IAAI,CAAC;EACpC;EAEAE,UAAUA,CAAA,EAAkB;IAC1B,OAAO,IAAI,CAACtB,QAAQ,CAACG,OAAO;EAC9B;EAESoB,EAAEA,CAACC,KAAa,EAAEC,QAA8B,EAAO;IAC9D,OAAO,IAAI,CAACzB,QAAQ,CAACuB,EAAE,CAACC,KAAK,EAAEC,QAAQ,CAAC;EAC1C;EAESC,GAAGA,CAACF,KAAa,EAAEC,QAA8B,EAAO;IAC/D,OAAO,IAAI,CAACzB,QAAQ,CAAC0B,GAAG,CAACF,KAAK,EAAEC,QAAQ,CAAC;EAC3C;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sourceRoot":"../../src","sources":["types.ts"],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const COINBASE_METHODS = {
|
|
2
|
+
REQUEST_ACCOUNTS: 'eth_requestAccounts',
|
|
3
|
+
SIGN_TRANSACTION: 'eth_signTransaction',
|
|
4
|
+
SEND_TRANSACTION: 'eth_sendTransaction',
|
|
5
|
+
SIGN_MESSAGE: 'personal_sign',
|
|
6
|
+
SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3',
|
|
7
|
+
SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4',
|
|
8
|
+
SWITCH_CHAIN: 'wallet_switchEthereumChain',
|
|
9
|
+
ADD_ETHEREUM_CHAIN: 'wallet_addEthereumChain',
|
|
10
|
+
WATCH_ASSET: 'wallet_watchAsset'
|
|
11
|
+
};
|
|
12
|
+
export function isValidMethod(method) {
|
|
13
|
+
return Object.values(COINBASE_METHODS).includes(method);
|
|
14
|
+
}
|
|
15
|
+
export function getCoinbaseNamespace(namespaces, accounts) {
|
|
16
|
+
if (!namespaces || !accounts) {
|
|
17
|
+
throw new Error('CoinbaseConnector: Namespaces or accounts not found');
|
|
18
|
+
}
|
|
19
|
+
const namespace = namespaces['eip155'];
|
|
20
|
+
if (!namespace) {
|
|
21
|
+
throw new Error('CoinbaseConnector: Namespace not found');
|
|
22
|
+
}
|
|
23
|
+
let caipAddresses = [];
|
|
24
|
+
for (const account of accounts) {
|
|
25
|
+
namespace.chains?.forEach(chain => {
|
|
26
|
+
caipAddresses.push(`${chain}:${account}`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
['eip155']: {
|
|
31
|
+
...namespace,
|
|
32
|
+
methods: Object.values(COINBASE_METHODS),
|
|
33
|
+
accounts: caipAddresses
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function numberToHex(value) {
|
|
38
|
+
// Convert string to number first, then to hex
|
|
39
|
+
const num = typeof value === 'string' ? parseInt(value, 10) : value;
|
|
40
|
+
return `0x${num.toString(16)}`;
|
|
41
|
+
}
|
|
42
|
+
export function hexToString(hexValue) {
|
|
43
|
+
// Remove 0x prefix if present
|
|
44
|
+
const cleanHex = hexValue.startsWith('0x') ? hexValue.slice(2) : hexValue;
|
|
45
|
+
// Convert hex to decimal number, then to string
|
|
46
|
+
|
|
47
|
+
return parseInt(cleanHex, 16).toString();
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["COINBASE_METHODS","REQUEST_ACCOUNTS","SIGN_TRANSACTION","SEND_TRANSACTION","SIGN_MESSAGE","SIGN_TYPED_DATA_V3","SIGN_TYPED_DATA_V4","SWITCH_CHAIN","ADD_ETHEREUM_CHAIN","WATCH_ASSET","isValidMethod","method","Object","values","includes","getCoinbaseNamespace","namespaces","accounts","Error","namespace","caipAddresses","account","chains","forEach","chain","push","methods","numberToHex","value","num","parseInt","toString","hexToString","hexValue","cleanHex","startsWith","slice"],"sourceRoot":"../../src","sources":["utils.ts"],"mappings":"AAOA,OAAO,MAAMA,gBAAgB,GAAG;EAC9BC,gBAAgB,EAAE,qBAAqB;EACvCC,gBAAgB,EAAE,qBAAqB;EACvCC,gBAAgB,EAAE,qBAAqB;EACvCC,YAAY,EAAE,eAAe;EAC7BC,kBAAkB,EAAE,sBAAsB;EAC1CC,kBAAkB,EAAE,sBAAsB;EAC1CC,YAAY,EAAE,4BAA4B;EAC1CC,kBAAkB,EAAE,yBAAyB;EAC7CC,WAAW,EAAE;AACf,CAAU;AAEV,OAAO,SAASC,aAAaA,CAACC,MAAc,EAA4B;EACtE,OAAOC,MAAM,CAACC,MAAM,CAACb,gBAAgB,CAAC,CAACc,QAAQ,CAACH,MAAwB,CAAC;AAC3E;AAEA,OAAO,SAASI,oBAAoBA,CAClCC,UAA+B,EAC/BC,QAAmB,EACP;EACZ,IAAI,CAACD,UAAU,IAAI,CAACC,QAAQ,EAAE;IAC5B,MAAM,IAAIC,KAAK,CAAC,qDAAqD,CAAC;EACxE;EAEA,MAAMC,SAAS,GAAGH,UAAU,CAAC,QAAQ,CAAC;EAEtC,IAAI,CAACG,SAAS,EAAE;IACd,MAAM,IAAID,KAAK,CAAC,wCAAwC,CAAC;EAC3D;EAEA,IAAIE,aAA4B,GAAG,EAAE;EAErC,KAAK,MAAMC,OAAO,IAAIJ,QAAQ,EAAE;IAC9BE,SAAS,CAACG,MAAM,EAAEC,OAAO,CAACC,KAAK,IAAI;MACjCJ,aAAa,CAACK,IAAI,CAAE,GAAED,KAAM,IAAGH,OAAQ,EAAC,CAAC;IAC3C,CAAC,CAAC;EACJ;EAEA,OAAO;IACL,CAAC,QAAQ,GAAG;MACV,GAAGF,SAAS;MACZO,OAAO,EAAEd,MAAM,CAACC,MAAM,CAACb,gBAAgB,CAAC;MACxCiB,QAAQ,EAAEG;IACZ;EACF,CAAC;AACH;AAEA,OAAO,SAASO,WAAWA,CAACC,KAAsB,EAAE;EAClD;EACA,MAAMC,GAAG,GAAG,OAAOD,KAAK,KAAK,QAAQ,GAAGE,QAAQ,CAACF,KAAK,EAAE,EAAE,CAAC,GAAGA,KAAK;EAEnE,OAAQ,KAAIC,GAAG,CAACE,QAAQ,CAAC,EAAE,CAAE,EAAC;AAChC;AAEA,OAAO,SAASC,WAAWA,CAACC,QAAgB,EAAE;EAC5C;EACA,MAAMC,QAAQ,GAAGD,QAAQ,CAACE,UAAU,CAAC,IAAI,CAAC,GAAGF,QAAQ,CAACG,KAAK,CAAC,CAAC,CAAC,GAAGH,QAAQ;EACzE;;EAEA,OAAOH,QAAQ,CAACI,QAAQ,EAAE,EAAE,CAAC,CAACH,QAAQ,CAAC,CAAC;AAC1C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { WalletConnector, type AppKitNetwork, type CaipNetworkId, type ConnectionProperties, type ConnectOptions, type ConnectorInitOptions, type Namespaces, type WalletInfo } from '@reown/appkit-common-react-native';
|
|
2
|
+
import { CoinbaseProvider } from '../providers/CoinbaseProvider';
|
|
3
|
+
import type { CoinbaseConnectorConfig } from '../types';
|
|
4
|
+
export declare class CoinbaseConnector extends WalletConnector {
|
|
5
|
+
private static readonly SUPPORTED_NAMESPACE;
|
|
6
|
+
private config;
|
|
7
|
+
constructor(config: CoinbaseConnectorConfig);
|
|
8
|
+
init(ops: ConnectorInitOptions): Promise<void>;
|
|
9
|
+
connect(opts?: ConnectOptions): Promise<Namespaces | undefined>;
|
|
10
|
+
disconnect(): Promise<void>;
|
|
11
|
+
getProvider(): CoinbaseProvider;
|
|
12
|
+
getNamespaces(): Namespaces;
|
|
13
|
+
getChainId(): CaipNetworkId | undefined;
|
|
14
|
+
getWalletInfo(): WalletInfo | undefined;
|
|
15
|
+
getProperties(): ConnectionProperties | undefined;
|
|
16
|
+
switchNetwork(network: AppKitNetwork): Promise<void>;
|
|
17
|
+
private deleteSession;
|
|
18
|
+
private saveSession;
|
|
19
|
+
restoreSession(): Promise<boolean>;
|
|
20
|
+
private getStorage;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=CoinbaseConnector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoinbaseConnector.d.ts","sourceRoot":"","sources":["../../../src/connectors/CoinbaseConnector.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,KAAK,aAAa,EAClB,KAAK,aAAa,EAElB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,UAAU,EAEhB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,uBAAuB,EAAmB,MAAM,UAAU,CAAC;AAIzE,qBAAa,iBAAkB,SAAQ,eAAe;IACpD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAA4B;IACvE,OAAO,CAAC,MAAM,CAA0B;gBAE5B,MAAM,EAAE,uBAAuB;IAK5B,IAAI,CAAC,GAAG,EAAE,oBAAoB;IAgB9B,OAAO,CAAC,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAU/D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC,WAAW,IAAI,gBAAgB;IAQ/B,aAAa,IAAI,UAAU;IAQ3B,UAAU,IAAI,aAAa,GAAG,SAAS;IAOvC,aAAa,IAAI,UAAU,GAAG,SAAS;IASvC,aAAa,IAAI,oBAAoB,GAAG,SAAS;IAI3C,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCnE,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,WAAW;IAKJ,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAajD,OAAO,CAAC,UAAU;CAOnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import EventEmitter from 'events';
|
|
3
|
+
import type { Provider, RequestArguments } from '@reown/appkit-common-react-native';
|
|
4
|
+
import type { CoinbaseProviderConfig } from '../types';
|
|
5
|
+
export declare class CoinbaseProvider extends EventEmitter implements Provider {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private provider;
|
|
8
|
+
constructor(config: CoinbaseProviderConfig);
|
|
9
|
+
connect<T = string[]>(): Promise<T>;
|
|
10
|
+
disconnect(): Promise<void>;
|
|
11
|
+
request<T = unknown>(args: RequestArguments): Promise<T>;
|
|
12
|
+
getChainId(): `0x${string}`;
|
|
13
|
+
on(event: string, listener: (args?: any) => void): any;
|
|
14
|
+
off(event: string, listener: (args?: any) => void): any;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=CoinbaseProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CoinbaseProvider.d.ts","sourceRoot":"","sources":["../../../src/providers/CoinbaseProvider.ts"],"names":[],"mappings":";AAAA,OAAO,YAAY,MAAM,QAAQ,CAAC;AAClC,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGpF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD,qBAAa,gBAAiB,SAAQ,YAAa,YAAW,QAAQ;IACpE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,QAAQ,CAA6B;gBAEjC,MAAM,EAAE,sBAAsB;IAgBpC,OAAO,CAAC,CAAC,GAAG,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;IAuBnC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;IAQxD,UAAU,IAAI,KAAK,MAAM,EAAE;IAIlB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,GAAG;IAItD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,GAAG;CAGjE"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Namespaces } from '@reown/appkit-common-react-native';
|
|
2
|
+
import type { KVStorage, WalletMobileSDKProviderOptions } from '@coinbase/wallet-mobile-sdk/build/WalletMobileSDKEVMProvider';
|
|
3
|
+
import type { COINBASE_METHODS } from './utils';
|
|
4
|
+
export type CoinbaseProviderConfig = Omit<WalletMobileSDKProviderOptions, 'chainId' | 'jsonRpcUrl' | 'address'> & {
|
|
5
|
+
defaultChain?: number;
|
|
6
|
+
redirect: string;
|
|
7
|
+
rpcUrl?: string;
|
|
8
|
+
};
|
|
9
|
+
export type Values<T> = T[keyof T];
|
|
10
|
+
export type CoinbaseMethod = Values<typeof COINBASE_METHODS>;
|
|
11
|
+
export type CoinbaseSession = {
|
|
12
|
+
namespaces: Namespaces;
|
|
13
|
+
};
|
|
14
|
+
export type CoinbaseConnectorConfig = {
|
|
15
|
+
storage?: KVStorage;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,KAAK,EACV,SAAS,EACT,8BAA8B,EAC/B,MAAM,8DAA8D,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,MAAM,sBAAsB,GAAG,IAAI,CACvC,8BAA8B,EAC9B,SAAS,GAAG,YAAY,GAAG,SAAS,CACrC,GAAG;IACF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnC,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE7D,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Namespaces, ProposalNamespaces } from '@reown/appkit-common-react-native';
|
|
2
|
+
import type { CoinbaseMethod } from './types';
|
|
3
|
+
export declare const COINBASE_METHODS: {
|
|
4
|
+
readonly REQUEST_ACCOUNTS: "eth_requestAccounts";
|
|
5
|
+
readonly SIGN_TRANSACTION: "eth_signTransaction";
|
|
6
|
+
readonly SEND_TRANSACTION: "eth_sendTransaction";
|
|
7
|
+
readonly SIGN_MESSAGE: "personal_sign";
|
|
8
|
+
readonly SIGN_TYPED_DATA_V3: "eth_signTypedData_v3";
|
|
9
|
+
readonly SIGN_TYPED_DATA_V4: "eth_signTypedData_v4";
|
|
10
|
+
readonly SWITCH_CHAIN: "wallet_switchEthereumChain";
|
|
11
|
+
readonly ADD_ETHEREUM_CHAIN: "wallet_addEthereumChain";
|
|
12
|
+
readonly WATCH_ASSET: "wallet_watchAsset";
|
|
13
|
+
};
|
|
14
|
+
export declare function isValidMethod(method: string): method is CoinbaseMethod;
|
|
15
|
+
export declare function getCoinbaseNamespace(namespaces?: ProposalNamespaces, accounts?: string[]): Namespaces;
|
|
16
|
+
export declare function numberToHex(value: string | number): string;
|
|
17
|
+
export declare function hexToString(hexValue: string): string;
|
|
18
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,UAAU,EACV,kBAAkB,EACnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,eAAO,MAAM,gBAAgB;;;;;;;;;;CAUnB,CAAC;AAEX,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,cAAc,CAEtE;AAED,wBAAgB,oBAAoB,CAClC,UAAU,CAAC,EAAE,kBAAkB,EAC/B,QAAQ,CAAC,EAAE,MAAM,EAAE,GAClB,UAAU,CA0BZ;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,UAKjD;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,UAM3C"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reown/appkit-coinbase-react-native",
|
|
3
|
+
"version": "0.0.0-feat-coinbase-20250722173213",
|
|
4
|
+
"main": "lib/commonjs/index.js",
|
|
5
|
+
"types": "lib/typescript/index.d.ts",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
|
+
"source": "src/index.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "bob build",
|
|
10
|
+
"clean": "rm -rf lib",
|
|
11
|
+
"test": "jest --passWithNoTests",
|
|
12
|
+
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src",
|
|
16
|
+
"lib"
|
|
17
|
+
],
|
|
18
|
+
"keywords": [
|
|
19
|
+
"web3",
|
|
20
|
+
"crypto",
|
|
21
|
+
"appkit",
|
|
22
|
+
"walletconnect",
|
|
23
|
+
"react-native",
|
|
24
|
+
"coinbase"
|
|
25
|
+
],
|
|
26
|
+
"repository": "https://github.com/reown-com/appkit-react-native",
|
|
27
|
+
"author": "Reown <support@reown.com> (https://reown.com)",
|
|
28
|
+
"homepage": "https://reown.com/appkit",
|
|
29
|
+
"license": "Apache-2.0",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/reown-com/appkit-react-native/issues"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"registry": "https://registry.npmjs.org/",
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@reown/appkit-common-react-native": "0.0.0-feat-coinbase-20250722173213"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@coinbase/wallet-mobile-sdk": ">=1.1.2"
|
|
42
|
+
},
|
|
43
|
+
"react-native": "src/index.ts",
|
|
44
|
+
"react-native-builder-bob": {
|
|
45
|
+
"source": "src",
|
|
46
|
+
"output": "lib",
|
|
47
|
+
"targets": [
|
|
48
|
+
"commonjs",
|
|
49
|
+
"module",
|
|
50
|
+
[
|
|
51
|
+
"typescript",
|
|
52
|
+
{
|
|
53
|
+
"tsc": "../../node_modules/.bin/tsc"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"eslintIgnore": [
|
|
59
|
+
"node_modules/",
|
|
60
|
+
"lib/"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {
|
|
2
|
+
WalletConnector,
|
|
3
|
+
type AppKitNetwork,
|
|
4
|
+
type CaipNetworkId,
|
|
5
|
+
type ChainNamespace,
|
|
6
|
+
type ConnectionProperties,
|
|
7
|
+
type ConnectOptions,
|
|
8
|
+
type ConnectorInitOptions,
|
|
9
|
+
type Namespaces,
|
|
10
|
+
type WalletInfo,
|
|
11
|
+
type Storage
|
|
12
|
+
} from '@reown/appkit-common-react-native';
|
|
13
|
+
import { getCoinbaseNamespace, hexToString, numberToHex } from '../utils';
|
|
14
|
+
import { CoinbaseProvider } from '../providers/CoinbaseProvider';
|
|
15
|
+
import type { CoinbaseConnectorConfig, CoinbaseSession } from '../types';
|
|
16
|
+
|
|
17
|
+
const SESSION_KEY = '@appkit/coinbase-connector/session';
|
|
18
|
+
|
|
19
|
+
export class CoinbaseConnector extends WalletConnector {
|
|
20
|
+
private static readonly SUPPORTED_NAMESPACE: ChainNamespace = 'eip155';
|
|
21
|
+
private config: CoinbaseConnectorConfig;
|
|
22
|
+
|
|
23
|
+
constructor(config: CoinbaseConnectorConfig) {
|
|
24
|
+
super({ type: 'coinbase' });
|
|
25
|
+
this.config = config;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
override async init(ops: ConnectorInitOptions) {
|
|
29
|
+
super.init(ops);
|
|
30
|
+
|
|
31
|
+
if (!ops.metadata.redirect?.universal) {
|
|
32
|
+
throw new Error('CoinbaseConnector: Universal link not found in metadata');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.provider = new CoinbaseProvider({
|
|
36
|
+
redirect: ops.metadata.redirect.universal,
|
|
37
|
+
// use config storage only, as it needs to be mmkv-compatible
|
|
38
|
+
storage: this.config.storage
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await this.restoreSession();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override async connect(opts?: ConnectOptions): Promise<Namespaces | undefined> {
|
|
45
|
+
const accounts = await this.getProvider().connect();
|
|
46
|
+
|
|
47
|
+
const namespaces = getCoinbaseNamespace(opts?.namespaces, accounts);
|
|
48
|
+
this.namespaces = namespaces;
|
|
49
|
+
this.saveSession(namespaces);
|
|
50
|
+
|
|
51
|
+
return this.namespaces;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
override async disconnect(): Promise<void> {
|
|
55
|
+
await super.disconnect();
|
|
56
|
+
this.deleteSession();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override getProvider(): CoinbaseProvider {
|
|
60
|
+
if (!this.provider) {
|
|
61
|
+
throw new Error('CoinbaseConnector: Provider not initialized');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return this.provider as CoinbaseProvider;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override getNamespaces(): Namespaces {
|
|
68
|
+
if (!this.namespaces) {
|
|
69
|
+
throw new Error('CoinbaseConnector: Namespaces not initialized');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return this.namespaces;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override getChainId(): CaipNetworkId | undefined {
|
|
76
|
+
const hexChainId = this.getProvider().getChainId();
|
|
77
|
+
const chainId = hexToString(hexChainId);
|
|
78
|
+
|
|
79
|
+
return `${CoinbaseConnector.SUPPORTED_NAMESPACE}:${chainId}` as CaipNetworkId;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override getWalletInfo(): WalletInfo | undefined {
|
|
83
|
+
// TODO: Add icon
|
|
84
|
+
return {
|
|
85
|
+
name: 'Coinbase Wallet',
|
|
86
|
+
description: 'Your home to everything onchain',
|
|
87
|
+
url: 'https://www.coinbase.com/wallet'
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override getProperties(): ConnectionProperties | undefined {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
override async switchNetwork(network: AppKitNetwork): Promise<void> {
|
|
96
|
+
const provider = this.getProvider();
|
|
97
|
+
const chainId_ = numberToHex(network.id);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await provider.request({
|
|
101
|
+
method: 'wallet_switchEthereumChain',
|
|
102
|
+
params: [{ chainId: chainId_ }]
|
|
103
|
+
});
|
|
104
|
+
} catch (error: any) {
|
|
105
|
+
// Indicates chain is not added to provider
|
|
106
|
+
if (error?.code === 4902) {
|
|
107
|
+
try {
|
|
108
|
+
await provider.request({
|
|
109
|
+
method: 'wallet_addEthereumChain',
|
|
110
|
+
params: [
|
|
111
|
+
{
|
|
112
|
+
chainId: chainId_,
|
|
113
|
+
chainName: network.name,
|
|
114
|
+
nativeCurrency: network.nativeCurrency,
|
|
115
|
+
rpcUrls: [network.rpcUrls.default?.http[0] ?? ''],
|
|
116
|
+
blockExplorerUrls: [network.blockExplorers?.default.url]
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
});
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.warn('CoinbaseConnector: switchNetwork error', e);
|
|
122
|
+
throw e;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
console.warn('CoinbaseConnector: switchNetwork error', error);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private deleteSession(): void {
|
|
131
|
+
const storage = this.getStorage();
|
|
132
|
+
storage.removeItem(SESSION_KEY);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private saveSession(namespaces: Namespaces): void {
|
|
136
|
+
const storage = this.getStorage();
|
|
137
|
+
storage.setItem(SESSION_KEY, JSON.stringify({ namespaces }));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
override async restoreSession(): Promise<boolean> {
|
|
141
|
+
const storage = this.getStorage();
|
|
142
|
+
const session = await storage.getItem<string>(SESSION_KEY);
|
|
143
|
+
if (!session) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const { namespaces } = JSON.parse(session) as CoinbaseSession;
|
|
148
|
+
this.namespaces = namespaces;
|
|
149
|
+
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private getStorage(): Storage {
|
|
154
|
+
if (!this.storage) {
|
|
155
|
+
throw new Error('CoinbaseConnector: Storage not initialized');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return this.storage;
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import EventEmitter from 'events';
|
|
2
|
+
import type { Provider, RequestArguments } from '@reown/appkit-common-react-native';
|
|
3
|
+
import { configure, WalletMobileSDKEVMProvider } from '@coinbase/wallet-mobile-sdk';
|
|
4
|
+
import { isValidMethod } from '../utils';
|
|
5
|
+
import type { CoinbaseProviderConfig } from '../types';
|
|
6
|
+
|
|
7
|
+
export class CoinbaseProvider extends EventEmitter implements Provider {
|
|
8
|
+
private readonly config: CoinbaseProviderConfig;
|
|
9
|
+
private provider: WalletMobileSDKEVMProvider;
|
|
10
|
+
|
|
11
|
+
constructor(config: CoinbaseProviderConfig) {
|
|
12
|
+
super();
|
|
13
|
+
this.config = config;
|
|
14
|
+
configure({
|
|
15
|
+
hostURL: new URL('https://wallet.coinbase.com/wsegue'),
|
|
16
|
+
callbackURL: new URL(this.config.redirect), // App Universal Link
|
|
17
|
+
hostPackageName: 'org.toshi'
|
|
18
|
+
});
|
|
19
|
+
this.provider = new WalletMobileSDKEVMProvider({
|
|
20
|
+
...this.config,
|
|
21
|
+
jsonRpcUrl: this.config.rpcUrl,
|
|
22
|
+
chainId: this.config.defaultChain,
|
|
23
|
+
storage: this.config.storage
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async connect<T = string[]>(): Promise<T> {
|
|
28
|
+
try {
|
|
29
|
+
let accounts: string[] = [];
|
|
30
|
+
const isConnected = this.provider.connected;
|
|
31
|
+
|
|
32
|
+
if (!isConnected) {
|
|
33
|
+
accounts = await this.provider.request({
|
|
34
|
+
method: 'eth_requestAccounts',
|
|
35
|
+
params: []
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
accounts = this.provider.selectedAddress ? [this.provider.selectedAddress] : [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//TODO: check switch chain
|
|
42
|
+
|
|
43
|
+
return accounts as T;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.warn('CoinbaseProvider: connect error', error);
|
|
46
|
+
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async disconnect(): Promise<void> {
|
|
51
|
+
this.provider.disconnect();
|
|
52
|
+
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
request<T = unknown>(args: RequestArguments): Promise<T> {
|
|
57
|
+
if (!isValidMethod(args.method)) {
|
|
58
|
+
throw new Error(`CoinbaseProvider: Invalid method: ${args.method}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return this.provider.request(args);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getChainId(): `0x${string}` {
|
|
65
|
+
return this.provider.chainId as `0x${string}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override on(event: string, listener: (args?: any) => void): any {
|
|
69
|
+
return this.provider.on(event, listener);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override off(event: string, listener: (args?: any) => void): any {
|
|
73
|
+
return this.provider.off(event, listener);
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Namespaces } from '@reown/appkit-common-react-native';
|
|
2
|
+
import type {
|
|
3
|
+
KVStorage,
|
|
4
|
+
WalletMobileSDKProviderOptions
|
|
5
|
+
} from '@coinbase/wallet-mobile-sdk/build/WalletMobileSDKEVMProvider';
|
|
6
|
+
import type { COINBASE_METHODS } from './utils';
|
|
7
|
+
|
|
8
|
+
export type CoinbaseProviderConfig = Omit<
|
|
9
|
+
WalletMobileSDKProviderOptions,
|
|
10
|
+
'chainId' | 'jsonRpcUrl' | 'address'
|
|
11
|
+
> & {
|
|
12
|
+
defaultChain?: number;
|
|
13
|
+
redirect: string;
|
|
14
|
+
rpcUrl?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type Values<T> = T[keyof T];
|
|
18
|
+
|
|
19
|
+
export type CoinbaseMethod = Values<typeof COINBASE_METHODS>;
|
|
20
|
+
|
|
21
|
+
export type CoinbaseSession = {
|
|
22
|
+
namespaces: Namespaces;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type CoinbaseConnectorConfig = {
|
|
26
|
+
storage?: KVStorage;
|
|
27
|
+
};
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CaipAddress,
|
|
3
|
+
Namespaces,
|
|
4
|
+
ProposalNamespaces
|
|
5
|
+
} from '@reown/appkit-common-react-native';
|
|
6
|
+
import type { CoinbaseMethod } from './types';
|
|
7
|
+
|
|
8
|
+
export const COINBASE_METHODS = {
|
|
9
|
+
REQUEST_ACCOUNTS: 'eth_requestAccounts',
|
|
10
|
+
SIGN_TRANSACTION: 'eth_signTransaction',
|
|
11
|
+
SEND_TRANSACTION: 'eth_sendTransaction',
|
|
12
|
+
SIGN_MESSAGE: 'personal_sign',
|
|
13
|
+
SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3',
|
|
14
|
+
SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4',
|
|
15
|
+
SWITCH_CHAIN: 'wallet_switchEthereumChain',
|
|
16
|
+
ADD_ETHEREUM_CHAIN: 'wallet_addEthereumChain',
|
|
17
|
+
WATCH_ASSET: 'wallet_watchAsset'
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
export function isValidMethod(method: string): method is CoinbaseMethod {
|
|
21
|
+
return Object.values(COINBASE_METHODS).includes(method as CoinbaseMethod);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getCoinbaseNamespace(
|
|
25
|
+
namespaces?: ProposalNamespaces,
|
|
26
|
+
accounts?: string[]
|
|
27
|
+
): Namespaces {
|
|
28
|
+
if (!namespaces || !accounts) {
|
|
29
|
+
throw new Error('CoinbaseConnector: Namespaces or accounts not found');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const namespace = namespaces['eip155'];
|
|
33
|
+
|
|
34
|
+
if (!namespace) {
|
|
35
|
+
throw new Error('CoinbaseConnector: Namespace not found');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let caipAddresses: CaipAddress[] = [];
|
|
39
|
+
|
|
40
|
+
for (const account of accounts) {
|
|
41
|
+
namespace.chains?.forEach(chain => {
|
|
42
|
+
caipAddresses.push(`${chain}:${account}`);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
['eip155']: {
|
|
48
|
+
...namespace,
|
|
49
|
+
methods: Object.values(COINBASE_METHODS),
|
|
50
|
+
accounts: caipAddresses
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function numberToHex(value: string | number) {
|
|
56
|
+
// Convert string to number first, then to hex
|
|
57
|
+
const num = typeof value === 'string' ? parseInt(value, 10) : value;
|
|
58
|
+
|
|
59
|
+
return `0x${num.toString(16)}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function hexToString(hexValue: string) {
|
|
63
|
+
// Remove 0x prefix if present
|
|
64
|
+
const cleanHex = hexValue.startsWith('0x') ? hexValue.slice(2) : hexValue;
|
|
65
|
+
// Convert hex to decimal number, then to string
|
|
66
|
+
|
|
67
|
+
return parseInt(cleanHex, 16).toString();
|
|
68
|
+
}
|