Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions packages/provider/src/modules/rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { BaseModule } from './base-module';
import { createInvalidArgumentError, MagicRPCError, createSynchronousWeb3MethodWarning } from '../core/sdk-exceptions';
import { createJsonRpcRequestPayload, standardizeJsonRpcRequestPayload, JsonRpcResponse } from '../core/json-rpc';
import { PromiEvent } from '../util/promise-tools';
import { createTypedEmitter, TypedEmitter } from '../util/events';
import { createTypedEmitter, EventsDefinition, TypedEmitter } from '../util/events';

const { createBoundEmitterMethod, createChainingEmitterMethod } = createTypedEmitter();

Expand Down Expand Up @@ -45,7 +45,11 @@ export class RPCProviderModule extends BaseModule implements TypedEmitter {
.post(
this.overlay,
MagicOutgoingWindowMessage.MAGIC_HANDLE_REQUEST,
payload.map((p) => standardizeJsonRpcRequestPayload(p)),
payload.map((p) => {
const standardizedPayload = standardizeJsonRpcRequestPayload(p);
this.prefixPayloadMethodForTestMode(standardizedPayload);
return standardizedPayload;
}),
)
.then((batchResponse) => {
(onRequestComplete as JsonRpcBatchRequestCallback)(
Expand All @@ -58,6 +62,7 @@ export class RPCProviderModule extends BaseModule implements TypedEmitter {
});
} else {
const finalPayload = standardizeJsonRpcRequestPayload(payload);
this.prefixPayloadMethodForTestMode(finalPayload);
this.transport
.post(this.overlay, MagicOutgoingWindowMessage.MAGIC_HANDLE_REQUEST, finalPayload)
.then((response) => {
Expand Down Expand Up @@ -85,7 +90,6 @@ export class RPCProviderModule extends BaseModule implements TypedEmitter {
payloadOrMethod,
Array.isArray(onRequestCompleteOrParams) ? onRequestCompleteOrParams : [],
);

return this.request(payload) as any;
}

Expand All @@ -112,6 +116,30 @@ export class RPCProviderModule extends BaseModule implements TypedEmitter {
return this.request<string[]>(requestPayload);
}

/**
* Here, we wrap `BaseModule.request` with an additional check
* to determine if the RPC method requires a test-mode prefix.
*/
protected request<ResultType = any, Events extends EventsDefinition = void>(payload: Partial<JsonRpcRequestPayload>) {
this.prefixPayloadMethodForTestMode(payload);
return super.request<ResultType, Events>(payload);
}

/**
* Prefixes Ethereum RPC methods with a `testMode` identifier. This is done so
* that Magic's <iframe> can handle signing methods using test-specific keys.
*/
private prefixPayloadMethodForTestMode(payload: Partial<JsonRpcRequestPayload>) {
const testModePrefix = 'testMode/eth/';

// In test mode, we prefix all RPC methods with `test/` so that the
// Magic <iframe> can handle them without requiring network calls.
if (this.sdk.testMode) {
// eslint-disable-next-line no-param-reassign
payload.method = `${testModePrefix}${payload.method}`;
}
}

public on = createChainingEmitterMethod('on', this);
public once = createChainingEmitterMethod('once', this);
public addListener = createChainingEmitterMethod('addListener', this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable global-require */

import browserEnv from '@ikscodes/browser-env';
import test from 'ava';
import sinon from 'sinon';
import { JsonRpcRequestPayload } from '@magic-sdk/types';
import { createMagicSDK, createMagicSDKTestMode } from '../../../factories';
import { BaseModule } from '../../../../src/modules/base-module';

const requestPayload: JsonRpcRequestPayload = {
jsonrpc: '2.0',
id: 1,
params: [],
method: 'foobar',
};

test.beforeEach((t) => {
browserEnv.restore();
(BaseModule as any).prototype.request = sinon.stub();
});

test.serial('Calls `BaseModule.request` WITHOUT test-mode prefix', async (t) => {
const magic = createMagicSDK();

await (magic.rpcProvider as any).request(requestPayload);

t.deepEqual((BaseModule as any).prototype.request.args[0][0], requestPayload);
});

test.serial('Calls `BaseModule.request` WITH test-mode prefix', async (t) => {
const magic = createMagicSDKTestMode();

await (magic.rpcProvider as any).request(requestPayload);

t.deepEqual((BaseModule as any).prototype.request.args[0][0], { ...requestPayload, method: 'testMode/eth/foobar' });
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import browserEnv from '@ikscodes/browser-env';
import test from 'ava';
import sinon from 'sinon';
import { createMagicSDK } from '../../../factories';
import { createMagicSDK, createMagicSDKTestMode } from '../../../factories';
import { getPayloadIdStub } from '../../../mocks';
import { BaseModule } from '../../../../src/modules/base-module';
import { RPCProviderModule } from '../../../../src/modules/rpc-provider';
import { createSynchronousWeb3MethodWarning } from '../../../../src/core/sdk-exceptions';

test.beforeEach((t) => {
browserEnv.restore();
(BaseModule as any).prototype.request = sinon.stub();
(RPCProviderModule as any).prototype.request = sinon.stub();
(RPCProviderModule as any).prototype.sendAsync = sinon.stub();
});

Expand Down