Skip to content

Commit 27a981c

Browse files
committed
Added load balancer support to PocketProvider (ethers-io#1052).
1 parent 29be1e3 commit 27a981c

File tree

2 files changed

+138
-26
lines changed

2 files changed

+138
-26
lines changed

packages/providers/src.ts/pocket-provider.ts

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

3-
import { Network } from "@ethersproject/networks";
3+
import { Network, Networkish } from "@ethersproject/networks";
4+
import { getStatic } from "@ethersproject/properties";
45
import { ConnectionInfo } from "@ethersproject/web";
56

67
import { Logger } from "@ethersproject/logger";
@@ -9,20 +10,63 @@ const logger = new Logger(version);
910

1011
import { UrlJsonRpcProvider } from "./url-json-rpc-provider";
1112

12-
const defaultApplicationId = "5f7f8547b90218002e9ce9dd";
13+
// These are load-balancer-based applicatoin IDs
14+
const defaultApplicationIds: Record<string, string> = {
15+
homestead: "6004bcd10040261633ade990",
16+
ropsten: "6004bd4d0040261633ade991",
17+
rinkeby: "6004bda20040261633ade994",
18+
goerli: "6004bd860040261633ade992",
19+
};
1320

1421
export class PocketProvider extends UrlJsonRpcProvider {
1522
readonly applicationId: string;
1623
readonly applicationSecretKey: string;
24+
readonly loadBalancer: boolean;
25+
26+
constructor(network?: Networkish, apiKey?: any) {
27+
// We need a bit of creativity in the constructor because
28+
// Pocket uses different default API keys based on the network
29+
30+
if (apiKey == null) {
31+
const n = getStatic<(network: Networkish) => Network>(new.target, "getNetwork")(network);
32+
if (n) {
33+
const applicationId = defaultApplicationIds[n.name];
34+
if (applicationId) {
35+
apiKey = {
36+
applicationId: applicationId,
37+
loadBalancer: true
38+
};
39+
}
40+
}
41+
42+
// If there was any issue above, we don't know this network
43+
if (apiKey == null) {
44+
logger.throwError("unsupported network", Logger.errors.INVALID_ARGUMENT, {
45+
argument: "network",
46+
value: network
47+
});
48+
}
49+
50+
}
51+
52+
super(network, apiKey);
53+
}
1754

1855
static getApiKey(apiKey: any): any {
19-
const apiKeyObj: { applicationId: string, applicationSecretKey: string } = {
20-
applicationId: defaultApplicationId,
56+
// Most API Providers allow null to get the default configuration, but
57+
// Pocket requires the network to decide the default provider, so we
58+
// rely on hijacking the constructor to add a sensible default for us
59+
60+
if (apiKey == null) {
61+
logger.throwArgumentError("PocketProvider.getApiKey does not support null apiKey", "apiKey", apiKey);
62+
}
63+
64+
const apiKeyObj: { applicationId: string, applicationSecretKey: string, loadBalancer: boolean } = {
65+
applicationId: null,
66+
loadBalancer: false,
2167
applicationSecretKey: null
2268
};
2369

24-
if (apiKey == null) { return apiKeyObj; }
25-
2670
// Parse applicationId and applicationSecretKey
2771
if (typeof (apiKey) === "string") {
2872
apiKeyObj.applicationId = apiKey;
@@ -35,9 +79,17 @@ export class PocketProvider extends UrlJsonRpcProvider {
3579

3680
apiKeyObj.applicationId = apiKey.applicationId;
3781
apiKeyObj.applicationSecretKey = apiKey.applicationSecretKey;
82+
apiKeyObj.loadBalancer = !!apiKey.loadBalancer;
3883

3984
} else if (apiKey.applicationId) {
85+
logger.assertArgument((typeof (apiKey.applicationId) === "string"),
86+
"apiKey.applicationId must be a string", "apiKey.applicationId", apiKey.applicationId);
87+
4088
apiKeyObj.applicationId = apiKey.applicationId;
89+
apiKeyObj.loadBalancer = !!apiKey.loadBalancer;
90+
91+
} else {
92+
logger.throwArgumentError("unsupported PocketProvider apiKey", "apiKey", apiKey);
4193
}
4294

4395
return apiKeyObj;
@@ -49,16 +101,30 @@ export class PocketProvider extends UrlJsonRpcProvider {
49101
case "homestead":
50102
host = "eth-mainnet.gateway.pokt.network";
51103
break;
104+
case "ropsten":
105+
host = "eth-ropsten.gateway.pokt.network";
106+
break;
107+
case "rinkeby":
108+
host = "eth-rinkeby.gateway.pokt.network";
109+
break;
110+
case "goerli":
111+
host = "eth-goerli.gateway.pokt.network";
112+
break;
52113
default:
53114
logger.throwError("unsupported network", Logger.errors.INVALID_ARGUMENT, {
54115
argument: "network",
55116
value: network
56117
});
57118
}
58119

59-
const connection: ConnectionInfo = {
60-
url: (`https:/\/${ host }/v1/${ apiKey.applicationId }`),
61-
};
120+
let url = null;
121+
if (apiKey.loadBalancer) {
122+
url = `https:/\/${ host }/v1/lb/${ apiKey.applicationId }`
123+
} else {
124+
url = `https:/\/${ host }/v1/${ apiKey.applicationId }`
125+
}
126+
127+
const connection: ConnectionInfo = { url };
62128

63129
// Initialize empty headers
64130
connection.headers = {}
@@ -73,6 +139,6 @@ export class PocketProvider extends UrlJsonRpcProvider {
73139
}
74140

75141
isCommunityResource(): boolean {
76-
return (this.applicationId === defaultApplicationId);
142+
return (this.applicationId === defaultApplicationIds[this.network.name]);
77143
}
78144
}

packages/tests/src.ts/test-providers.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -434,32 +434,54 @@ type TestDescription = {
434434

435435
const allNetworks = [ "default", "homestead", "ropsten", "rinkeby", "kovan", "goerli" ];
436436

437-
const ApiKeys: Record<string, string> = {
437+
// We use separate API keys because otherwise the testcases sometimes
438+
// fail during CI because our default keys are pretty heavily used
439+
const _ApiKeys: Record<string, string> = {
438440
alchemy: "YrPw6SWb20vJDRFkhWq8aKnTQ8JRNRHM",
439441
etherscan: "FPFGK6JSW2UHJJ2666FG93KP7WC999MNW7",
440442
infura: "49a0efa3aaee4fd99797bfa94d8ce2f1",
441-
pocket: "5f7f8547b90218002e9ce9dd",
442443
};
443444

445+
const _ApiKeysPocket: Record<string, string> = {
446+
homestead: "6004bcd10040261633ade990",
447+
ropsten: "6004bd4d0040261633ade991",
448+
rinkeby: "6004bda20040261633ade994",
449+
goerli: "6004bd860040261633ade992",
450+
};
451+
452+
type ApiKeySet = {
453+
alchemy: string;
454+
etherscan: string;
455+
infura: string;
456+
pocket: string;
457+
};
458+
459+
function getApiKeys(network: string): ApiKeySet {
460+
if (network === "default" || network == null) { network = "homestead"; }
461+
const apiKeys = ethers.utils.shallowCopy(_ApiKeys);
462+
apiKeys.pocket = _ApiKeysPocket[network];
463+
return <ApiKeySet>apiKeys;
464+
}
465+
444466
const providerFunctions: Array<ProviderDescription> = [
445467
{
446468
name: "getDefaultProvider",
447469
networks: allNetworks,
448470
create: (network: string) => {
449471
if (network == "default") {
450-
return ethers.getDefaultProvider(null, ApiKeys);
472+
return ethers.getDefaultProvider(null, getApiKeys(network));
451473
}
452-
return ethers.getDefaultProvider(network, ApiKeys);
474+
return ethers.getDefaultProvider(network, getApiKeys(network));
453475
}
454476
},
455477
{
456478
name: "AlchemyProvider",
457479
networks: allNetworks,
458480
create: (network: string) => {
459481
if (network == "default") {
460-
return new ethers.providers.AlchemyProvider(null, ApiKeys.alchemy);
482+
return new ethers.providers.AlchemyProvider(null, getApiKeys(network).alchemy);
461483
}
462-
return new ethers.providers.AlchemyProvider(network, ApiKeys.alchemy);
484+
return new ethers.providers.AlchemyProvider(network, getApiKeys(network).alchemy);
463485
}
464486
},
465487
/*
@@ -476,19 +498,19 @@ const providerFunctions: Array<ProviderDescription> = [
476498
networks: allNetworks,
477499
create: (network: string) => {
478500
if (network == "default") {
479-
return new ethers.providers.InfuraProvider(null, ApiKeys.infura);
501+
return new ethers.providers.InfuraProvider(null, getApiKeys(network).infura);
480502
}
481-
return new ethers.providers.InfuraProvider(network, ApiKeys.infura);
503+
return new ethers.providers.InfuraProvider(network, getApiKeys(network).infura);
482504
}
483505
},
484506
{
485507
name: "EtherscanProvider",
486508
networks: allNetworks,
487509
create: (network: string) => {
488510
if (network == "default") {
489-
return new ethers.providers.EtherscanProvider(null, ApiKeys.etherscan);
511+
return new ethers.providers.EtherscanProvider(null, getApiKeys(network).etherscan);
490512
}
491-
return new ethers.providers.EtherscanProvider(network, ApiKeys.etherscan);
513+
return new ethers.providers.EtherscanProvider(network, getApiKeys(network).etherscan);
492514
}
493515
},
494516
{
@@ -500,12 +522,19 @@ const providerFunctions: Array<ProviderDescription> = [
500522
},
501523
{
502524
name: "PocketProvider",
503-
networks: [ "default", "homestead" ],
525+
// note: sans-kovan
526+
networks: [ "default", "homestead", "ropsten", "rinkeby", "goerli" ],
504527
create: (network: string) => {
505528
if (network == "default") {
506-
return new ethers.providers.PocketProvider(null, ApiKeys.pocket);
529+
return new ethers.providers.PocketProvider(null, {
530+
applicationId: getApiKeys(network).pocket,
531+
loadBalancer: true
532+
});
507533
}
508-
return new ethers.providers.PocketProvider(network, ApiKeys.pocket);
534+
return new ethers.providers.PocketProvider(network, {
535+
applicationId: getApiKeys(network).pocket,
536+
loadBalancer: true
537+
});
509538
}
510539
},
511540
{
@@ -720,7 +749,7 @@ describe("Test Provider Methods", function() {
720749
this.timeout(300000);
721750

722751
// Get some ether from the faucet
723-
const provider = new ethers.providers.InfuraProvider("ropsten", ApiKeys.infura);
752+
const provider = new ethers.providers.InfuraProvider("ropsten", getApiKeys("ropsten").infura);
724753
const funder = await ethers.utils.fetchJson(`https:/\/api.ethers.io/api/v1/?action=fundAccount&address=${ fundWallet.address.toLowerCase() }`);
725754
fundReceipt = provider.waitForTransaction(funder.hash);
726755
fundReceipt.then((receipt) => {
@@ -735,7 +764,7 @@ describe("Test Provider Methods", function() {
735764
await fundReceipt;
736765

737766
// Refund all unused ether to the faucet
738-
const provider = new ethers.providers.InfuraProvider("ropsten", ApiKeys.infura);
767+
const provider = new ethers.providers.InfuraProvider("ropsten", getApiKeys("ropsten").infura);
739768
const gasPrice = await provider.getGasPrice();
740769
const balance = await provider.getBalance(fundWallet.address);
741770
const tx = await fundWallet.connect(provider).sendTransaction({
@@ -806,7 +835,7 @@ describe("Extra tests", function() {
806835
it("etherscan long-request #1093", async function() {
807836
this.timeout(60000);
808837
await waiter(2000);
809-
const provider = new ethers.providers.EtherscanProvider(null, ApiKeys.etherscan);
838+
const provider = new ethers.providers.EtherscanProvider(null, getApiKeys(null).etherscan);
810839
const value = await provider.call({
811840
to: "0xbf320b8336b131e0270295c15478d91741f9fc11",
812841
data: "0x3ad206cc000000000000000000000000f6e914d07d12636759868a61e52973d17ed7111b0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006400000000000000000000000022b3faaa8df978f6bafe18aade18dc2e3dfa0e0c000000000000000000000000998b3b82bc9dba173990be7afb772788b5acb8bd000000000000000000000000ba11d00c5f74255f56a5e366f4f77f5a186d7f55000000000000000000000000c7579bb99af590ec71c316e1ac4436c5350395940000000000000000000000002a05d22db079bc40c2f77a1d1ff703a56e631cc10000000000000000000000000d8775f648430679a709e98d2b0cb6250d2887ef0000000000000000000000009a0242b7a33dacbe40edb927834f96eb39f8fbcb000000000000000000000000c78593c17482ea5de44fdd84896ffd903972878e000000000000000000000000e7d3e4413e29ae35b0893140f4500965c74365e500000000000000000000000037d40510a2f5bc98aa7a0f7bf4b3453bcfb90ac10000000000000000000000004a6058666cf1057eac3cd3a5a614620547559fc900000000000000000000000035a69642857083ba2f30bfab735dacc7f0bac96900000000000000000000000084f7c44b6fed1080f647e354d552595be2cc602f0000000000000000000000001500205f50bf3fd976466d0662905c9ff254fc9c000000000000000000000000660b612ec57754d949ac1a09d0c2937a010dee05000000000000000000000000acfa209fb73bf3dd5bbfb1101b9bc999c49062a5000000000000000000000000865d176351f287fe1b0010805b110d08699c200a000000000000000000000000633a8f8e557702039463f9f2eb20b7936fff8c050000000000000000000000001961b3331969ed52770751fc718ef530838b6dee0000000000000000000000002fb12bccf6f5dd338b76be784a93ade0724256900000000000000000000000004d8fc1453a0f359e99c9675954e656d80d996fbf0000000000000000000000006aeb95f06cda84ca345c2de0f3b7f96923a44f4c0000000000000000000000008aa33a7899fcc8ea5fbe6a608a109c3893a1b8b200000000000000000000000014c926f2290044b647e1bf2072e67b495eff1905000000000000000000000000763186eb8d4856d536ed4478302971214febc6a90000000000000000000000008a1e3930fde1f151471c368fdbb39f3f63a65b55000000000000000000000000a8daa52ded91f7c82b4bb02b4b87c6a841db1fd500000000000000000000000033803edf44a71b9579f54cd429b53b06c0eeab83000000000000000000000000026e62dded1a6ad07d93d39f96b9eabd59665e0d00000000000000000000000047da42696a866cdc61a4c809a515500a242909c100000000000000000000000008b4c866ae9d1be56a06e0c302054b4ffe067b43000000000000000000000000420335d3deef2d5b87524ff9d0fb441f71ea621f000000000000000000000000983f7cc12d0b5d512b0f91f51a4aa478ac4def46000000000000000000000000b2bfeb70b903f1baac7f2ba2c62934c7e5b974c40000000000000000000000009b11b1b271a224a271619f3419b1b080fdec5b4a0000000000000000000000007b1309c1522afd4e66c31e1e6d0ec1319e1eba5e000000000000000000000000959529102cfde07b1196bd27adedc196d75f84f6000000000000000000000000107c4504cd79c5d2696ea0030a8dd4e92601b82e000000000000000000000000539efe69bcdd21a83efd9122571a64cc25e0282b000000000000000000000000e5a7c12972f3bbfe70ed29521c8949b8af6a0970000000000000000000000000f8ad7dfe656188a23e89da09506adf7ad9290d5d0000000000000000000000005732046a883704404f284ce41ffadd5b007fd668000000000000000000000000df6ef343350780bf8c3410bf062e0c015b1dd671000000000000000000000000f028adee51533b1b47beaa890feb54a457f51e89000000000000000000000000dd6bf56ca2ada24c683fac50e37783e55b57af9f000000000000000000000000ef51c9377feb29856e61625caf9390bd0b67ea18000000000000000000000000c80c5e40220172b36adee2c951f26f2a577810c50000000000000000000000001f573d6fb3f13d689ff844b4ce37794d79a7ff1c000000000000000000000000d2d6158683aee4cc838067727209a0aaf4359de30000000000000000000000007cdec53fe4770729dac314756c10e2f37b8d2b2f000000000000000000000000cc34366e3842ca1bd36c1f324d15257960fcc8010000000000000000000000006b01c3170ae1efebee1a3159172cb3f7a5ecf9e5000000000000000000000000139d9397274bb9e2c29a9aa8aa0b5874d30d62e300000000000000000000000063f584fa56e60e4d0fe8802b27c7e6e3b33e007f000000000000000000000000780116d91e5592e58a3b3c76a351571b39abcec60000000000000000000000000e511aa1a137aad267dfe3a6bfca0b856c1a3682000000000000000000000000327682779bab2bf4d1337e8974ab9de8275a7ca80000000000000000000000001b80eeeadcc590f305945bcc258cfa770bbe18900000000000000000000000005af2be193a6abca9c8817001f45744777db307560000000000000000000000009e77d5a1251b6f7d456722a6eac6d2d5980bd891000000000000000000000000e25f0974fea47682f6a7386e4217da70512ec997000000000000000000000000558ec3152e2eb2174905cd19aea4e34a23de9ad6000000000000000000000000b736ba66aad83adb2322d1f199bfa32b3962f13c000000000000000000000000509a38b7a1cc0dcd83aa9d06214663d9ec7c7f4a0000000000000000000000000327112423f3a68efdf1fcf402f6c5cb9f7c33fd0000000000000000000000005acd19b9c91e596b1f062f18e3d02da7ed8d1e5000000000000000000000000003df4c372a29376d2c8df33a1b5f001cd8d68b0e0000000000000000000000006aac8cb9861e42bf8259f5abdc6ae3ae89909e11000000000000000000000000d96b9fd7586d9ea24c950d24399be4fb65372fdd00000000000000000000000073dd069c299a5d691e9836243bcaec9c8c1d87340000000000000000000000005ecd84482176db90bb741ddc8c2f9ccc290e29ce000000000000000000000000fa456cf55250a839088b27ee32a424d7dacb54ff000000000000000000000000b683d83a532e2cb7dfa5275eed3698436371cc9f000000000000000000000000ccbf21ba6ef00802ab06637896b799f7101f54a20000000000000000000000007b123f53421b1bf8533339bfbdc7c98aa94163db0000000000000000000000006ecccf7ebc3497a9334f4fe957a7d5fa933c5bcc0000000000000000000000004fabb145d64652a948d72533023f6e7a623c7c53000000000000000000000000e1aee98495365fc179699c1bb3e761fa716bee6200000000000000000000000056d811088235f11c8920698a204a5010a788f4b300000000000000000000000026e75307fc0c021472feb8f727839531f112f3170000000000000000000000007d4b8cce0591c9044a22ee543533b72e976e36c30000000000000000000000003c6a7ab47b5f058be0e7c7fe1a4b7925b8aca40e0000000000000000000000001d462414fe14cf489c7a21cac78509f4bf8cd7c000000000000000000000000043044f861ec040db59a7e324c40507addb67314200000000000000000000000004f2e7221fdb1b52a68169b25793e51478ff0329000000000000000000000000954b890704693af242613edef1b603825afcd708000000000000000000000000a8f93faee440644f89059a2c88bdc9bf3be5e2ea0000000000000000000000001234567461d3f8db7496581774bd869c83d51c9300000000000000000000000056ba2ee7890461f463f7be02aac3099f6d5811a80000000000000000000000006c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e000000000000000000000000f444cd92e09cc8b2a23cd2eecb3c1e4cc8da6958000000000000000000000000cf8f9555d55ce45a3a33a81d6ef99a2a2e71dee2000000000000000000000000076c97e1c869072ee22f8c91978c99b4bcb0259100000000000000000000000017b26400621695c2d8c2d8869f6259e82d7544c4000000000000000000000000679badc551626e01b23ceecefbc9b877ea18fc46000000000000000000000000336f646f87d9f6bc6ed42dd46e8b3fd9dbd15c220000000000000000000000005d3a536e4d6dbd6114cc1ead35777bab948e3643000000000000000000000000f5dce57282a584d2746faf1593d3121fcac444dc0000000000000000000000001d9e20e581a5468644fe74ccb6a46278ef377f9e000000000000000000000000177d39ac676ed1c67a2b268ad7f1e58826e5b0af"
@@ -983,6 +1012,23 @@ describe("Test API Key Formatting", function() {
9831012
assert.equal(apiKeyObject2.applicationId, applicationId);
9841013
assert.equal(apiKeyObject2.applicationSecretKey, applicationSecretKey);
9851014

1015+
// Test complex API key with loadBalancer
1016+
[ true, false ].forEach((loadBalancer) => {
1017+
const apiKeyObject = ethers.providers.PocketProvider.getApiKey({
1018+
applicationId, loadBalancer
1019+
});
1020+
assert.equal(apiKeyObject.applicationId, applicationId);
1021+
assert.equal(apiKeyObject.loadBalancer, loadBalancer);
1022+
assert.ok(apiKeyObject.applicationSecretKey == null);
1023+
1024+
const apiKeyObject2 = ethers.providers.PocketProvider.getApiKey({
1025+
applicationId, applicationSecretKey, loadBalancer
1026+
});
1027+
assert.equal(apiKeyObject2.applicationId, applicationId);
1028+
assert.equal(apiKeyObject2.applicationSecretKey, applicationSecretKey);
1029+
assert.equal(apiKeyObject2.loadBalancer, loadBalancer);
1030+
});
1031+
9861032
// Fails on invalid applicationId type
9871033
assert.throws(() => {
9881034
const apiKey = ethers.providers.PocketProvider.getApiKey({

0 commit comments

Comments
 (0)