Skip to content

Commit

Permalink
remove dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
bangtoven committed Jan 19, 2024
1 parent f6308f5 commit cefccf6
Show file tree
Hide file tree
Showing 7 changed files with 350 additions and 969 deletions.
3 changes: 1 addition & 2 deletions react-native/client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/wallet-mobile-sdk",
"version": "1.0.10",
"version": "1.0.12",
"description": "Coinbase Wallet Mobile SDK for React Native",
"main": "build/CoinbaseWalletSDK.js",
"types": "build/CoinbaseWalletSDK.d.ts",
Expand All @@ -27,7 +27,6 @@
"license": "Apache-2.0",
"homepage": "https://github.com/coinbase/wallet-mobile-sdk#readme",
"dependencies": {
"@coinbase/wallet-sdk": "^3.5.1",
"@metamask/safe-event-emitter": "2.0.0",
"eth-rpc-errors": "4.0.3",
"buffer": "6.0.3",
Expand Down
69 changes: 40 additions & 29 deletions react-native/client/src/WalletMobileSDKEVMProvider.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import {
RequestArguments,
Web3Provider,
} from "@coinbase/wallet-sdk/dist/provider/Web3Provider";
} from "./types/provider/Web3Provider";
import {
JSONRPCMethod,
JSONRPCRequest,
JSONRPCResponse,
} from "@coinbase/wallet-sdk/dist/provider/JSONRPC";
} from "./types/provider/JSONRPC";
import {
AddressString,
Callback,
IntNumber,
} from "@coinbase/wallet-sdk/dist/types";
} from "./types/core/type";
import { ethErrors } from "eth-rpc-errors";
import {
initiateHandshake,
Expand All @@ -30,8 +29,7 @@ import {
hexStringFromBuffer,
hexStringFromIntNumber,
prepend0x,
} from "@coinbase/wallet-sdk/dist/util";
import { EthereumTransactionParams } from "@coinbase/wallet-sdk/dist/relay/EthereumTransactionParams";
} from "./types/core/util";
import BN from "bn.js";
import { MMKV, NativeMMKV } from "react-native-mmkv";
import SafeEventEmitter from "@metamask/safe-event-emitter";
Expand Down Expand Up @@ -78,6 +76,19 @@ interface WatchAssetParams {
};
}

interface EthereumTransactionParams {
fromAddress: AddressString;
toAddress: AddressString | null;
weiValue: BN;
data: Buffer;
nonce: IntNumber | null;
gasPriceInWei: BN | null;
maxFeePerGas: BN | null; // in wei
maxPriorityFeePerGas: BN | null; // in wei
gasLimit: BN | null;
chainId: IntNumber;
}

export class WalletMobileSDKEVMProvider
extends SafeEventEmitter
implements Web3Provider
Expand Down Expand Up @@ -147,7 +158,7 @@ export class WalletMobileSDKEVMProvider
resetSession();
this._addresses = [];
this._storage.delete(CACHED_ADDRESSES_KEY);
this.emit('disconnect');
this.emit("disconnect");
return true;
}

Expand Down Expand Up @@ -328,13 +339,13 @@ export class WalletMobileSDKEVMProvider

private _handleSynchronousMethods({ method }: JSONRPCRequest) {
switch (method) {
case JSONRPCMethod.eth_accounts:
case "eth_accounts":
return this._eth_accounts();
case JSONRPCMethod.eth_coinbase:
case "eth_coinbase":
return this._eth_coinbase();
case JSONRPCMethod.net_version:
case "net_version":
return this._net_version();
case JSONRPCMethod.eth_chainId:
case "eth_chainId":
return this._eth_chainId();
default:
return undefined;
Expand All @@ -348,23 +359,23 @@ export class WalletMobileSDKEVMProvider
const params = request.params || [];

switch (method) {
case JSONRPCMethod.eth_requestAccounts:
case "eth_requestAccounts":
return this._eth_requestAccounts();
case JSONRPCMethod.personal_sign:
case "personal_sign":
return this._personal_sign(params);
case JSONRPCMethod.eth_signTypedData_v3:
case "eth_signTypedData_v3":
return this._eth_signTypedData(params, "v3");
case JSONRPCMethod.eth_signTypedData_v4:
case "eth_signTypedData_v4":
return this._eth_signTypedData(params, "v4");
case JSONRPCMethod.eth_signTransaction:
case "eth_signTransaction":
return this._eth_signTransaction(params, false);
case JSONRPCMethod.eth_sendTransaction:
case "eth_sendTransaction":
return this._eth_signTransaction(params, true);
case JSONRPCMethod.wallet_switchEthereumChain:
case "wallet_switchEthereumChain":
return this._wallet_switchEthereumChain(params);
case JSONRPCMethod.wallet_addEthereumChain:
case "wallet_addEthereumChain":
return this._wallet_addEthereumChain(params);
case JSONRPCMethod.wallet_watchAsset:
case "wallet_watchAsset":
return this._wallet_watchAsset(params);
default:
if (this._jsonRpcUrl) {
Expand Down Expand Up @@ -395,7 +406,7 @@ export class WalletMobileSDKEVMProvider

private async _eth_requestAccounts(): Promise<JSONRPCResponse> {
const action: Action = {
method: JSONRPCMethod.eth_requestAccounts,
method: "eth_requestAccounts",
params: {},
};

Expand All @@ -414,7 +425,7 @@ export class WalletMobileSDKEVMProvider
const address = ensureAddressString(params[1]);

const action: Action = {
method: JSONRPCMethod.personal_sign,
method: "personal_sign",
params: {
message,
address,
Expand All @@ -440,8 +451,8 @@ export class WalletMobileSDKEVMProvider
const action: Action = {
method:
type === "v3"
? JSONRPCMethod.eth_signTypedData_v3
: JSONRPCMethod.eth_signTypedData_v4,
? "eth_signTypedData_v3"
: "eth_signTypedData_v4",
params: {
address,
typedDataJson,
Expand All @@ -464,8 +475,8 @@ export class WalletMobileSDKEVMProvider
const tx = this._prepareTransactionParams((params[0] as any) || {});
const action: Action = {
method: shouldSubmit
? JSONRPCMethod.eth_sendTransaction
: JSONRPCMethod.eth_signTransaction,
? "eth_sendTransaction"
: "eth_signTransaction",
params: {
fromAddress: tx.fromAddress,
toAddress: tx.toAddress,
Expand Down Expand Up @@ -557,7 +568,7 @@ export class WalletMobileSDKEVMProvider
}

const action: Action = {
method: JSONRPCMethod.wallet_switchEthereumChain,
method: "wallet_switchEthereumChain",
params: {
chainId: chainId.toString(),
},
Expand Down Expand Up @@ -601,7 +612,7 @@ export class WalletMobileSDKEVMProvider
const chainIdNumber = parseInt(request.chainId, 16);

const action: Action = {
method: JSONRPCMethod.wallet_addEthereumChain,
method: "wallet_addEthereumChain",
params: {
chainId: chainIdNumber.toString(),
blockExplorerUrls: request.blockExplorerUrls ?? null,
Expand Down Expand Up @@ -653,7 +664,7 @@ export class WalletMobileSDKEVMProvider
const { address, symbol, image, decimals } = request.options;

const action: Action = {
method: JSONRPCMethod.wallet_watchAsset,
method: "wallet_watchAsset",
params: {
type: request.type,
options: {
Expand Down
38 changes: 38 additions & 0 deletions react-native/client/src/types/core/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2018-2023 Coinbase, Inc. <https://www.coinbase.com/>
// Licensed under the Apache License, version 2.0

interface Tag<T extends string, RealType> {
__tag__: T;
__realType__: RealType;
}

export type OpaqueType<T extends string, U> = U & Tag<T, U>;

export function OpaqueType<T extends Tag<string, unknown>>() {
return (value: T extends Tag<string, infer U> ? U : never): T => value as T;
}

export type HexString = OpaqueType<'HexString', string>;
export const HexString = OpaqueType<HexString>();

export type AddressString = OpaqueType<'AddressString', string>;
export const AddressString = OpaqueType<AddressString>();

export type BigIntString = OpaqueType<'BigIntString', string>;
export const BigIntString = OpaqueType<BigIntString>();

export type IntNumber = OpaqueType<'IntNumber', number>;
export function IntNumber(num: number): IntNumber {
return Math.floor(num) as IntNumber;
}

export type RegExpString = OpaqueType<'RegExpString', string>;
export const RegExpString = OpaqueType<RegExpString>();

export type Callback<T> = (err: Error | null, result: T | null) => void;

export enum ProviderType {
CoinbaseWallet = 'CoinbaseWallet',
MetaMask = 'MetaMask',
Unselected = '',
}
165 changes: 165 additions & 0 deletions react-native/client/src/types/core/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright (c) 2018-2023 Coinbase, Inc. <https://www.coinbase.com/>
// Licensed under the Apache License, version 2.0

import BN from 'bn.js';

import { AddressString, BigIntString, HexString, IntNumber, RegExpString } from './type';

const INT_STRING_REGEX = /^[0-9]*$/;
const HEXADECIMAL_STRING_REGEX = /^[a-f0-9]*$/;

export function hexStringToUint8Array(hexString: string): Uint8Array {
return new Uint8Array(hexString.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));
}

export function hexStringFromBuffer(buf: Buffer, includePrefix = false): HexString {
const hex = buf.toString('hex');
return HexString(includePrefix ? `0x${hex}` : hex);
}

export function bigIntStringFromBN(bn: BN): BigIntString {
return BigIntString(bn.toString(10));
}

export function intNumberFromHexString(hex: HexString): IntNumber {
return IntNumber(new BN(ensureEvenLengthHexString(hex, false), 16).toNumber());
}

export function hexStringFromIntNumber(num: IntNumber): HexString {
return HexString(`0x${new BN(num).toString(16)}`);
}

export function has0xPrefix(str: string): boolean {
return str.startsWith('0x') || str.startsWith('0X');
}

export function strip0x(hex: string): string {
if (has0xPrefix(hex)) {
return hex.slice(2);
}
return hex;
}

export function prepend0x(hex: string): string {
if (has0xPrefix(hex)) {
return `0x${hex.slice(2)}`;
}
return `0x${hex}`;
}

export function isHexString(hex: unknown): hex is HexString {
if (typeof hex !== 'string') {
return false;
}
const s = strip0x(hex).toLowerCase();
return HEXADECIMAL_STRING_REGEX.test(s);
}

class InvalidParamsError extends Error {
code = -32602;
constructor(message: string) {
super(message);
}
}

export function ensureHexString(hex: unknown, includePrefix = false): HexString {
if (typeof hex === 'string') {
const s = strip0x(hex).toLowerCase();
if (HEXADECIMAL_STRING_REGEX.test(s)) {
return HexString(includePrefix ? `0x${s}` : s);
}
}
throw new InvalidParamsError(`"${String(hex)}" is not a hexadecimal string`);
}

export function ensureEvenLengthHexString(hex: unknown, includePrefix = false): HexString {
let h = ensureHexString(hex, false);
if (h.length % 2 === 1) {
h = HexString(`0${h}`);
}
return includePrefix ? HexString(`0x${h}`) : h;
}

export function ensureAddressString(str: unknown): AddressString {
if (typeof str === 'string') {
const s = strip0x(str).toLowerCase();
if (isHexString(s) && s.length === 40) {
return AddressString(prepend0x(s));
}
}
throw new InvalidParamsError(`Invalid Ethereum address: ${String(str)}`);
}

export function ensureBuffer(str: unknown): Buffer {
if (Buffer.isBuffer(str)) {
return str;
}
if (typeof str === 'string') {
if (isHexString(str)) {
const s = ensureEvenLengthHexString(str, false);
return Buffer.from(s, 'hex');
}
return Buffer.from(str, 'utf8');
}
throw new InvalidParamsError(`Not binary data: ${String(str)}`);
}

export function ensureIntNumber(num: unknown): IntNumber {
if (typeof num === 'number' && Number.isInteger(num)) {
return IntNumber(num);
}
if (typeof num === 'string') {
if (INT_STRING_REGEX.test(num)) {
return IntNumber(Number(num));
}
if (isHexString(num)) {
return IntNumber(new BN(ensureEvenLengthHexString(num, false), 16).toNumber());
}
}
throw new InvalidParamsError(`Not an integer: ${String(num)}`);
}

export function ensureRegExpString(regExp: unknown): RegExpString {
if (regExp instanceof RegExp) {
return RegExpString(regExp.toString());
}
throw new InvalidParamsError(`Not a RegExp: ${String(regExp)}`);
}

export function ensureBN(val: unknown): BN {
if (val !== null && (BN.isBN(val) || isBigNumber(val))) {
return new BN((val as any).toString(10), 10);
}
if (typeof val === 'number') {
return new BN(ensureIntNumber(val));
}
if (typeof val === 'string') {
if (INT_STRING_REGEX.test(val)) {
return new BN(val, 10);
}
if (isHexString(val)) {
return new BN(ensureEvenLengthHexString(val, false), 16);
}
}
throw new InvalidParamsError(`Not an integer: ${String(val)}`);
}

export function ensureParsedJSONObject<T extends object>(val: unknown): T {
if (typeof val === 'string') {
return JSON.parse(val) as T;
}

if (typeof val === 'object') {
return val as T;
}

throw new InvalidParamsError(`Not a JSON string or an object: ${String(val)}`);
}

export function isBigNumber(val: unknown): boolean {
if (val == null || typeof (val as any).constructor !== 'function') {
return false;
}
const { constructor } = val as any;
return typeof constructor.config === 'function' && typeof constructor.EUCLID === 'number';
}
Loading

0 comments on commit cefccf6

Please sign in to comment.