From e236d2352e3fbb0f78965decac5893217347ceb7 Mon Sep 17 00:00:00 2001 From: Dhruv Kelawala Date: Mon, 8 Aug 2022 14:29:15 +0100 Subject: [PATCH] fix(sequenceProvider): feedergatewayUrl and gatewayUrl --- __tests__/sequencerProvider.test.ts | 32 ++++++++++++++++++++- src/index.ts | 1 + src/provider/sequencer.ts | 21 ++++++++++++-- src/utils/url.ts | 44 +++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/utils/url.ts diff --git a/__tests__/sequencerProvider.test.ts b/__tests__/sequencerProvider.test.ts index 72c3e5a88..766c13c1a 100644 --- a/__tests__/sequencerProvider.test.ts +++ b/__tests__/sequencerProvider.test.ts @@ -1,4 +1,5 @@ -import { SequencerProvider } from '../src'; +import { Contract, Provider, SequencerProvider, stark } from '../src'; +import { toBN } from '../src/utils/number'; import { compiledErc20, describeIfNotDevnet, @@ -8,9 +9,17 @@ import { describeIfSequencer('SequencerProvider', () => { let provider: SequencerProvider; + let customSequencerProvider: Provider; beforeAll(async () => { provider = getTestProvider() as SequencerProvider; + customSequencerProvider = new Provider({ + sequencer: { + baseUrl: 'https://alpha4.starknet.io', + feederGatewayUrl: 'feeder_gateway', + gatewayUrl: 'gateway', + }, // Similar to arguements used in docs + }); }); describe('Gateway specific methods', () => { @@ -42,4 +51,25 @@ describeIfSequencer('SequencerProvider', () => { }); }); }); + + describe('Test calls with Custom Sequencer Provider', () => { + let erc20: Contract; + const wallet = stark.randomAddress(); + + beforeAll(async () => { + const { contract_address, transaction_hash } = await customSequencerProvider.deployContract({ + contract: compiledErc20, + }); + + await customSequencerProvider.waitForTransaction(transaction_hash); + erc20 = new Contract(compiledErc20.abi, contract_address, customSequencerProvider); + }); + + test('Check ERC20 balance using Custom Sequencer Provider', async () => { + const result = await erc20.balance_of(wallet); + const [res] = result; + expect(res).toStrictEqual(toBN(0)); + expect(res).toStrictEqual(result.res); + }); + }); }); diff --git a/src/index.ts b/src/index.ts index bbec6f067..fd6460394 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,3 +22,4 @@ export * as uint256 from './utils/uint256'; export * as shortString from './utils/shortString'; export * as typedData from './utils/typedData'; export * from './utils/address'; +export * from './utils/url'; diff --git a/src/provider/sequencer.ts b/src/provider/sequencer.ts index ff00a84ce..ca8c105dc 100644 --- a/src/provider/sequencer.ts +++ b/src/provider/sequencer.ts @@ -31,6 +31,7 @@ import { BigNumberish, bigNumberishArrayToDecimalStringArray, toBN, toHex } from import { parseContract, wait } from '../utils/provider'; import { SequencerAPIResponseParser } from '../utils/responseParser/sequencer'; import { randomAddress } from '../utils/stark'; +import { isUrl } from '../utils/url'; import { GatewayError, HttpError } from './errors'; import { ProviderInterface } from './interface'; import { BlockIdentifier, getFormattedBlockIdentifier } from './utils'; @@ -74,9 +75,13 @@ export class SequencerProvider implements ProviderInterface { this.gatewayUrl = urljoin(this.baseUrl, 'gateway'); } else { this.baseUrl = optionsOrProvider.baseUrl; - this.feederGatewayUrl = - optionsOrProvider.feederGatewayUrl ?? urljoin(this.baseUrl, 'feeder_gateway'); - this.gatewayUrl = optionsOrProvider.gatewayUrl ?? urljoin(this.baseUrl, 'gateway'); + this.feederGatewayUrl = this.parseUrlOrPath( + this.baseUrl, + 'feeder_gateway', + optionsOrProvider.feederGatewayUrl + ); + this.gatewayUrl = this.parseUrlOrPath(this.baseUrl, 'gateway', optionsOrProvider.gatewayUrl); + this.chainId = optionsOrProvider.chainId ?? SequencerProvider.getChainIdFromBaseUrl(optionsOrProvider.baseUrl); @@ -106,6 +111,16 @@ export class SequencerProvider implements ProviderInterface { return StarknetChainId.TESTNET; } + private parseUrlOrPath(baseUrl: string, defaultPath: string, urlOrPath?: string) { + if (urlOrPath) { + if (isUrl(urlOrPath)) { + return urlOrPath; + } + return urljoin(baseUrl, urlOrPath); + } + return urljoin(baseUrl, defaultPath); + } + private getFetchUrl(endpoint: keyof Sequencer.Endpoints) { const gatewayUrlEndpoints = ['add_transaction']; diff --git a/src/utils/url.ts b/src/utils/url.ts new file mode 100644 index 000000000..101af6705 --- /dev/null +++ b/src/utils/url.ts @@ -0,0 +1,44 @@ +/** + * RegExps. + * A URL must match #1 and then at least one of #2/#3. + * Use two levels of REs to avoid REDOS. + */ + +/** + * Inspired from https://github.com/segmentio/is-url + */ + +const protocolAndDomainRE = /^(?:\w+:)?\/\/(\S+)$/; + +const localhostDomainRE = /^localhost[:?\d]*(?:[^:?\d]\S*)?$/; +const nonLocalhostDomainRE = /^[^\s.]+\.\S{2,}$/; + +/** + * Loosely validate a URL `string`. + * @param {String} s + * @return {Boolean} + */ +export function isUrl(s: string): boolean { + if (typeof s !== 'string') { + return false; + } + + const match = s.match(protocolAndDomainRE); + if (!match) { + return false; + } + + const everythingAfterProtocol = match[1]; + if (!everythingAfterProtocol) { + return false; + } + + if ( + localhostDomainRE.test(everythingAfterProtocol) || + nonLocalhostDomainRE.test(everythingAfterProtocol) + ) { + return true; + } + + return false; +}