Skip to content

Commit

Permalink
Merge branch 'main' into edkek/remove-react-workingdemo
Browse files Browse the repository at this point in the history
  • Loading branch information
ecp4224 authored Nov 15, 2024
2 parents 61af8d4 + 744ab6d commit 5eed82f
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 172 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/deploy-cdn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Deploy to CDN

on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to deploy'
required: true
default: ''

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Install Yarn 3
run: yarn set version 3.5.1

- name: Install Dependencies
run: yarn install --immutable

- name: Run deploy script
env:
tag: ${{ github.event.inputs.tag }}
run: bash ./scripts/deploy-cdn.sh

- name: Deploy dapps
uses: peaceiris/actions-gh-pages@068dc23d9710f1ba62e86896f84735d869951305
with:
personal_token: ${{ secrets.DEPLOY_TOKEN }}
# force_orphan: true # removing for now as it is incompatible with keep_files
keep_files: true # Important to keep the rest of the files deployed previously
publish_dir: ./deployments
43 changes: 43 additions & 0 deletions .github/workflows/deploy-static.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Deploy to CDN

on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to deploy'
required: true
default: ''

jobs:
deploy:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.inputs.tag }}

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Install Yarn 3
run: yarn set version 3.5.1

- name: Install Dependencies
run: yarn install --immutable

- name: Run deploy script
env:
tag: ${{ github.event.inputs.tag }}
run: bash ./scripts/deploy-static.sh

- name: Deploy dapps
uses: peaceiris/actions-gh-pages@068dc23d9710f1ba62e86896f84735d869951305
with:
personal_token: ${{ secrets.DEPLOY_TOKEN }}
# force_orphan: true # removing for now as it is incompatible with keep_files
keep_files: true # Important to keep the rest of the files deployed previously
publish_dir: ./deployments
28 changes: 23 additions & 5 deletions packages/sdk/src/services/Ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,37 @@ export class Ethereum {
autoRequestAccounts: false,
});

const proxiedProvieer = new Proxy(provider, {
// some common libraries, e.g. [email protected], can confict with our API.
const proxiedProvider = new Proxy(provider, {
// some common libraries, e.g. [email protected], can conflict with our API.
deleteProperty: () => true,
});

this.provider = proxiedProvieer;
this.provider = proxiedProvider;
this.sdkInstance = sdkInstance;

// Add try-catch block around window modifications
if (shouldSetOnWindow && typeof window !== 'undefined') {
setGlobalProvider(provider);
try {
setGlobalProvider(provider);
} catch (error) {
logger(
'[Ethereum] Unable to set global provider - window.ethereum may be read-only',
error,
);
// Continue execution without throwing
}
}

if (shouldShimWeb3 && typeof window !== 'undefined') {
shimWeb3(this.provider);
try {
shimWeb3(this.provider);
} catch (error) {
logger(
'[Ethereum] Unable to shim web3 - window.web3 may be read-only',
error,
);
// Continue execution without throwing
}
}

// Propagate display_uri events to the SDK
Expand Down
116 changes: 22 additions & 94 deletions packages/sdk/src/utils/get-browser-extension.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MetaMaskSDK } from '../sdk';
import { getBrowserExtension } from './get-browser-extension';
import { eip6963RequestProvider } from './eip6963RequestProvider';
import { getBrowserExtension } from './get-browser-extension';

jest.mock('./eip6963RequestProvider');

Expand All @@ -9,127 +9,55 @@ describe('getBrowserExtension', () => {

beforeEach(() => {
jest.clearAllMocks();

global.window = {} as any;
sdkInstance = {
options: {
dappMetadata: {},
},
platformManager: {
getPlatformType: jest.fn(),
},
options: { dappMetadata: {} },
platformManager: { getPlatformType: jest.fn() },
} as unknown as MetaMaskSDK;
});

it('should throw an error if window is undefined', async () => {
afterEach(() => {
global.window = undefined as any;
});

it('should throw if window is undefined', async () => {
global.window = undefined as any;
await expect(
getBrowserExtension({ mustBeMetaMask: true, sdkInstance }),
).rejects.toThrow('window not available');
});

it('should return baseProvider if eip6963RequestProvider resolves successfully', async () => {
it('should return provider from EIP-6963 if available', async () => {
const mockProvider = { isMetaMask: true };
global.window = { ethereum: {} } as any;
(eip6963RequestProvider as jest.Mock).mockResolvedValue(mockProvider);

const res = await getBrowserExtension({
mustBeMetaMask: true,
sdkInstance,
});

expect(res).toStrictEqual(mockProvider);
});

it('should throw an error if eip6963RequestProvider rejects and ethereum is not found in window object', async () => {
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);
global.window = {} as any;

await expect(
getBrowserExtension({ mustBeMetaMask: true, sdkInstance }),
).rejects.toThrow('Ethereum not found in window object');
});

it('should throw an error if no suitable provider is found', async () => {
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);
global.window = { ethereum: { providers: [] } } as any;

await expect(
getBrowserExtension({ mustBeMetaMask: true, sdkInstance }),
).rejects.toThrow('No suitable provider found');
});

it('should return MetaMask provider if mustBeMetaMask is true', async () => {
const mockProvider = { isMetaMask: true };
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);
global.window = { ethereum: { providers: [mockProvider] } } as any;

const res = await getBrowserExtension({
const result = await getBrowserExtension({
mustBeMetaMask: true,
sdkInstance,
});

expect(res).toStrictEqual(mockProvider);
expect(result).toStrictEqual(expect.objectContaining({ isMetaMask: true }));
});

it('should return the first provider if mustBeMetaMask is false', async () => {
it('should fallback to window.ethereum only if not requiring MetaMask', async () => {
const mockProvider = { isMetaMask: false };
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);
global.window = { ethereum: { providers: [mockProvider] } } as any;
(eip6963RequestProvider as jest.Mock).mockRejectedValue(new Error());
global.window = { ethereum: mockProvider } as any;

const res = await getBrowserExtension({
const result = await getBrowserExtension({
mustBeMetaMask: false,
sdkInstance,
});

expect(res).toStrictEqual(mockProvider);
});

it('should throw an error if mustBeMetaMask is true but MetaMask provider not found', async () => {
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
expect(result).toStrictEqual(
expect.objectContaining({ isMetaMask: false }),
);
global.window = { ethereum: { isMetaMask: false } } as any;

await expect(
getBrowserExtension({ mustBeMetaMask: true, sdkInstance }),
).rejects.toThrow('MetaMask provider not found in Ethereum');
});

it('should throw an error if mustBeMetaMask is true but uniswap wallet installed instead of Metamask', async () => {
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);

global.window = {
ethereum: { isMetaMask: true, isUniswapWallet: true },
} as any;
it('should throw if no provider found', async () => {
(eip6963RequestProvider as jest.Mock).mockRejectedValue(new Error());
global.window = {} as any;

await expect(
getBrowserExtension({ mustBeMetaMask: true, sdkInstance }),
).rejects.toThrow('MetaMask provider not found in Ethereum');
});

it('should return ethereum object if mustBeMetaMask is false and ethereum object exists', async () => {
const ethereumObj = { isMetaMask: true };
(eip6963RequestProvider as jest.Mock).mockRejectedValue(
new Error('Provider request failed'),
);
global.window = { ethereum: ethereumObj } as any;

const res = await getBrowserExtension({
mustBeMetaMask: false,
sdkInstance,
});

expect(res).toStrictEqual(ethereumObj);
getBrowserExtension({ mustBeMetaMask: false, sdkInstance }),
).rejects.toThrow('Provider not found');
});
});
83 changes: 10 additions & 73 deletions packages/sdk/src/utils/get-browser-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,85 +11,22 @@ export async function getBrowserExtension({
sdkInstance: MetaMaskSDK;
}): Promise<MetaMaskInpageProvider> {
if (typeof window === 'undefined') {
throw new Error(`window not available`);
throw new Error('window not available');
}

let extensionProvider: MetaMaskInpageProvider;

try {
extensionProvider = await eip6963RequestProvider();
// Try EIP-6963 first
const extensionProvider = await eip6963RequestProvider();
return wrapExtensionProvider({ provider: extensionProvider, sdkInstance });
} catch (e) {
const { ethereum } = window;

if (!ethereum) {
throw new Error('Ethereum not found in window object');
}

// The `providers` field is populated when CoinBase Wallet extension is also installed
// The expected object is an array of providers, the MetaMask provider is inside
// See https://docs.cloud.coinbase.com/wallet-sdk/docs/injected-provider-guidance for
if ('providers' in ethereum) {
if (Array.isArray(ethereum.providers)) {
const provider = mustBeMetaMask
? ethereum.providers.find((p: any) =>
isRealMetaMaskExtensionInstalled(p),
)
: ethereum.providers[0];

if (!provider) {
throw new Error('No suitable provider found');
}

return wrapExtensionProvider({ provider, sdkInstance });
}
} else if (mustBeMetaMask && !isRealMetaMaskExtensionInstalled(ethereum)) {
throw new Error('MetaMask provider not found in Ethereum');
// Legacy fallback only for non-MetaMask cases
if (!mustBeMetaMask && window.ethereum) {
return wrapExtensionProvider({
provider: window.ethereum,
sdkInstance,
});
}

return wrapExtensionProvider({
provider: ethereum,
sdkInstance,
});
throw new Error('Provider not found');
}
}

function isRealMetaMaskExtensionInstalled(eth: any) {
if (!eth.isMetaMask) {
return false;
}

// Brave tries to make itself look like MetaMask
// Could also try RPC `web3_clientVersion` if following is unreliable
if (eth.isBraveWallet && !eth._events && !eth._state) {
return false;
}

// Other wallets that try to look like MetaMask
const flags: string[] = [
'isApexWallet',
'isAvalanche',
'isBitKeep',
'isBlockWallet',
'isKuCoinWallet',
'isMathWallet',
'isOkxWallet',
'isOKExWallet',
'isOneInchIOSWallet',
'isOneInchAndroidWallet',
'isOpera',
'isPortal',
'isRabby',
'isTokenPocket',
'isTokenary',
'isUniswapWallet',
'isZerion',
];
for (const flag of flags) {
if (eth[flag]) {
return false;
}
}

return true;
}
Loading

0 comments on commit 5eed82f

Please sign in to comment.