Skip to content

Commit 38cc974

Browse files
codybornclaude
andauthored
Revert "fix: resolve RPC URLs per-chain via RPC_<chainId> env vars" (#672) (#673)
* Revert "fix: resolve RPC URLs per-chain via RPC_<chainId> env vars (#672)" This reverts commit 577e509. * chore: bump Lambda VERSION env vars to force redeploy of reverted code Set each handler's VERSION one above current main so the reverted (RPC_PREFIX_URL) Lambda code is picked up on deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: send x-internal-service-secret header on all RPC requests (#674) * feat: send x-internal-service-secret header on all RPC requests Add the `x-internal-service-secret` header (sourced from the new RPC_HEADER_SECRET env var) to RPC_HEADERS in lib/util/constants.ts, which is the single header object used at every StaticJsonRpcProvider construction site (check-order-status, gs-reaper, EventWatcherMap, shared providers). The header is omitted when RPC_HEADER_SECRET is unset (local dev / unit tests). Wire RPC_HEADER_SECRET through the CDK stacks (bin/app.ts): beta/prod Lambda env vars from the gouda-service-rpc-urls-2 secret, the integ-test e2e build, and the local dev stack. Kept out of the jsonRpcUrls CfnOutput so the secret is not exposed in CloudFormation outputs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore: source integ-test RPC_PREFIX_URL from the production RPC secret Pull RPC_PREFIX_URL from gouda-service-rpc-urls-2:RPC_PREFIX_URL in the integ-test build so the e2e tests hit the same internal RPC endpoint and carry a matching RPC_HEADER_SECRET, instead of the separate all/gouda-service/integ-test/rpc secret. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: use await import instead of require in rpc-headers test Avoids the @typescript-eslint/no-var-requires lint error flagged in review. Dynamic import + jest.resetModules() re-evaluates the module so the RPC_HEADER_SECRET branch is still exercised per case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 577e509 commit 38cc974

13 files changed

Lines changed: 110 additions & 68 deletions

File tree

.env.example

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
# RPC URL resolution: getRpcUrl(chainId) returns process.env[`RPC_${chainId}`].
2-
# Define one variable per supported chain (see lib/util/chain.ts SUPPORTED_CHAINS),
3-
# e.g. RPC_1, RPC_130, RPC_8453, RPC_42161, ...
4-
RPC_1=
1+
# RPC URL resolution: getRpcUrl(chainId) returns `${RPC_PREFIX_URL}/${chainId}`.
2+
RPC_PREFIX_URL=
3+
# Sent as the `x-internal-service-secret` header on all RPC requests (see RPC_HEADERS
4+
# in lib/util/constants.ts). Leave unset to omit the header (e.g. public RPC).
5+
RPC_HEADER_SECRET=
56

67
FAILED_EVENT_DESTINATION_ARN=
78

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ cdk deploy GoudaServiceStack # Deploy to AWS
4242
## Environment Variables
4343

4444
Required for deployment:
45-
- `RPC_<chainId>` - Per-chain RPC URL (e.g. `RPC_1`, `RPC_130`, `RPC_8453`). `getRpcUrl(chainId)` in `lib/Config.ts` reads `process.env[\`RPC_${chainId}\`]`. One variable required per chain in `SUPPORTED_CHAINS` (`lib/util/chain.ts`).
45+
- `RPC_PREFIX_URL` - Base RPC URL; `getRpcUrl(chainId)` in `lib/Config.ts` appends `/<chainId>`.
46+
- `RPC_HEADER_SECRET` - Value sent as the `x-internal-service-secret` header on all RPC requests (see `RPC_HEADERS` in `lib/util/constants.ts`). Omitted when unset.
4647
- `FAILED_EVENT_DESTINATION_ARN` - Failed event SNS ARN
4748

4849
For tests:

README.md

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,9 @@ To test your changes you must redeploy your service. The dev cycle is thus:
4747

4848
```
4949
FAILED_EVENT_DESTINATION_ARN=<>
50-
# Per-chain RPC URLs: one variable per supported chain (see lib/util/chain.ts).
51-
RPC_1=<>
52-
RPC_130=<>
53-
RPC_8453=<>
54-
RPC_42161=<>
55-
# ...etc.
50+
RPC_PREFIX_URL=<>
51+
# Optional: sent as the `x-internal-service-secret` header on all RPC requests.
52+
RPC_HEADER_SECRET=<>
5653
5754
# Only need these if testing against custom contract deployments
5855
DL_REACTOR_TENDERLY=<>

bin/app.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { CodeBuildStep, CodePipeline, CodePipelineSource } from 'aws-cdk-lib/pip
99
import { Construct } from 'constructs'
1010
import dotenv from 'dotenv'
1111
import 'source-map-support/register'
12-
import { SUPPORTED_CHAINS } from '../lib/util/chain'
1312
import { STAGE } from '../lib/util/stage'
1413
import { PROD_TABLE_CAPACITY } from './config'
1514
import { SERVICE_NAME } from './constants'
@@ -169,19 +168,20 @@ export class APIPipeline extends Stack {
169168
'arn:aws:secretsmanager:us-east-2:644039819003:secret:prod-priority-labs-cosigner-address-iarU6E',
170169
})
171170

172-
// Per-chain RPC URLs. The Lambda's getRpcUrl(chainId) reads `RPC_<chainId>`
173-
// from the environment; the secret is a JSON map keyed the same way
174-
// (e.g. RPC_1, RPC_130, RPC_8453).
175-
const jsonRpcUrls: { [chain: string]: string } = {}
176-
for (const chainId of SUPPORTED_CHAINS) {
177-
const key = `RPC_${chainId}`
178-
jsonRpcUrls[key] = jsonRpcProvidersSecret.secretValueFromJson(key).toString()
171+
// Shared prefix the Lambda's getRpcUrl appends the chainId to.
172+
const jsonRpcUrls: { [chain: string]: string } = {
173+
RPC_PREFIX_URL: jsonRpcProvidersSecret.secretValueFromJson('RPC_PREFIX_URL').toString(),
179174
}
180175

181176
new CfnOutput(this, 'jsonRpcUrls', {
182177
value: JSON.stringify(jsonRpcUrls),
183178
})
184179

180+
// Authenticates outbound RPC requests via the `x-internal-service-secret`
181+
// header (see RPC_HEADERS in lib/util/constants.ts). Kept out of the
182+
// CfnOutput above so the secret is not exposed in CloudFormation outputs.
183+
const rpcHeaderSecret = jsonRpcProvidersSecret.secretValueFromJson('RPC_HEADER_SECRET').toString()
184+
185185
// Beta us-east-2
186186
const betaUsEast2Stage = new APIStage(this, 'beta-us-east-2', {
187187
env: { account: '321377678687', region: 'us-east-2' },
@@ -190,6 +190,7 @@ export class APIPipeline extends Stack {
190190
stage: STAGE.BETA,
191191
envVars: {
192192
...jsonRpcUrls,
193+
RPC_HEADER_SECRET: rpcHeaderSecret,
193194
QUOTER_TENDERLY: tenderlySecrets.secretValueFromJson('QUOTER_TENDERLY').toString(),
194195
DL_REACTOR_TENDERLY: tenderlySecrets.secretValueFromJson('DL_REACTOR_TENDERLY').toString(),
195196
PERMIT2_TENDERLY: tenderlySecrets.secretValueFromJson('PERMIT2_TENDERLY').toString(),
@@ -229,6 +230,7 @@ export class APIPipeline extends Stack {
229230
stage: STAGE.PROD,
230231
envVars: {
231232
...jsonRpcUrls,
233+
RPC_HEADER_SECRET: rpcHeaderSecret,
232234
FILL_EVENT_DESTINATION_ARN: resourceArnSecret.secretValueFromJson('FILL_EVENT_DESTINATION_ARN_PROD').toString(),
233235
ACTIVE_ORDER_EVENT_DESTINATION_ARN: resourceArnSecret
234236
.secretValueFromJson('ACTIVE_ORDER_EVENT_DESTINATION_ARN_PROD')
@@ -305,8 +307,12 @@ export class APIPipeline extends Stack {
305307
value: `${stage}/gouda-service/integ-test/cosigner`,
306308
type: BuildEnvironmentVariableType.SECRETS_MANAGER,
307309
},
308-
RPC_1: {
309-
value: 'all/gouda-service/integ-test/rpc:RPC_1',
310+
RPC_PREFIX_URL: {
311+
value: 'gouda-service-rpc-urls-2:RPC_PREFIX_URL',
312+
type: BuildEnvironmentVariableType.SECRETS_MANAGER,
313+
},
314+
RPC_HEADER_SECRET: {
315+
value: 'gouda-service-rpc-urls-2:RPC_HEADER_SECRET',
310316
type: BuildEnvironmentVariableType.SECRETS_MANAGER,
311317
},
312318
TEST_WALLET_PK: {
@@ -327,7 +333,8 @@ export class APIPipeline extends Stack {
327333
'echo "TAPI_API_KEY=${TAPI_API_KEY}" >> .env',
328334
'echo "GPA_SERVICE_URL=${GPA_SERVICE_URL}" >> .env',
329335
'echo "COSIGNER_ADDRESS=${COSIGNER_ADDRESS}" >> .env',
330-
'echo "RPC_1=${RPC_1}" >> .env',
336+
'echo "RPC_PREFIX_URL=${RPC_PREFIX_URL}" >> .env',
337+
'echo "RPC_HEADER_SECRET=${RPC_HEADER_SECRET}" >> .env',
331338
'echo "TEST_WALLET_PK=${TEST_WALLET_PK}" >> .env',
332339
'echo "TEST_FILLER_PK=${TEST_FILLER_PK}" >> .env',
333340
'yarn install --network-concurrency 1 --skip-integrity-check',
@@ -355,10 +362,8 @@ const app = new cdk.App()
355362
// Local dev stack
356363
const envVars: { [key: string]: string } = {}
357364

358-
for (const chainId of SUPPORTED_CHAINS) {
359-
const key = `RPC_${chainId}`
360-
envVars[key] = process.env[key] || ''
361-
}
365+
envVars['RPC_PREFIX_URL'] = process.env['RPC_PREFIX_URL'] || ''
366+
envVars['RPC_HEADER_SECRET'] = process.env['RPC_HEADER_SECRET'] || ''
362367

363368
envVars['RPC_TENDERLY'] = process.env[`RPC_TENDERLY`] || ''
364369
envVars['DL_REACTOR_TENDERLY'] = process.env[`DL_REACTOR_TENDERLY`] || ''

bin/stacks/lambda-stack.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export class LambdaStack extends cdk.NestedStack {
129129
...props.envVars,
130130
stage: props.stage as STAGE,
131131
KMS_KEY_ID: kmsKey.keyId,
132-
VERSION: '8',
132+
VERSION: '9',
133133
NODE_OPTIONS: '--enable-source-maps',
134134
}
135135

@@ -165,7 +165,7 @@ export class LambdaStack extends cdk.NestedStack {
165165
...props.envVars,
166166
stage: props.stage as STAGE,
167167
KMS_KEY_ID: kmsKey.keyId,
168-
VERSION: '7',
168+
VERSION: '8',
169169
NODE_OPTIONS: '--enable-source-maps',
170170
},
171171
vpc,
@@ -206,7 +206,7 @@ export class LambdaStack extends cdk.NestedStack {
206206
...props.envVars,
207207
stage: props.stage as STAGE,
208208
KMS_KEY_ID: kmsKey.keyId,
209-
VERSION: '8',
209+
VERSION: '9',
210210
NODE_OPTIONS: '--enable-source-maps',
211211
REGION: this.region,
212212
}
@@ -279,7 +279,7 @@ export class LambdaStack extends cdk.NestedStack {
279279
...props.envVars,
280280
stage: props.stage as STAGE,
281281
KMS_KEY_ID: kmsKey.keyId,
282-
VERSION: '7',
282+
VERSION: '8',
283283
NODE_OPTIONS: '--enable-source-maps',
284284
},
285285
tracing: aws_lambda.Tracing.ACTIVE,
@@ -300,7 +300,7 @@ export class LambdaStack extends cdk.NestedStack {
300300
stage: props.stage as STAGE,
301301
...props.envVars,
302302
KMS_KEY_ID: kmsKey.keyId,
303-
VERSION: '7',
303+
VERSION: '8',
304304
NODE_OPTIONS: '--enable-source-maps',
305305
},
306306
logRetention: logRetentionDays(props.stage),
@@ -320,7 +320,7 @@ export class LambdaStack extends cdk.NestedStack {
320320
stage: props.stage as STAGE,
321321
KMS_KEY_ID: kmsKey.keyId,
322322
...props.envVars,
323-
VERSION: '7',
323+
VERSION: '8',
324324
NODE_OPTIONS: '--enable-source-maps',
325325
},
326326
logRetention: logRetentionDays(props.stage),

bin/stacks/reaper-stack.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import { Cluster, ContainerImage } from "aws-cdk-lib/aws-ecs";
44
import { Construct } from "constructs";
55
import { SERVICE_NAME } from "../constants";
66

7-
// Expect per-chain RPC env vars (`RPC_<chainId>`, e.g. RPC_1, RPC_130) to be
8-
// set in environmentVariables; the reaper calls getRpcUrl(chainId) which reads
9-
// the corresponding `RPC_<chainId>` variable.
7+
// Expect RPC_PREFIX_URL to be set in environmentVariables; the reaper calls
8+
// getRpcUrl(chainId) which appends the chainId to it.
109
export interface ReaperStackProps extends StackProps {
1110
environmentVariables: { [key: string]: string };
1211
}

lib/Config.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ type Config = {
66
}
77

88
/**
9-
* Resolve the RPC URL for a given chainId from the per-chain env var
10-
* `RPC_<chainId>` (e.g. RPC_1, RPC_130, RPC_8453). Throws if it is unset.
9+
* Resolve the RPC URL for a given chainId by appending it to RPC_PREFIX_URL.
10+
* Throws if the prefix is not set.
1111
*/
1212
export const getRpcUrl = (chainId: number): string => {
13-
const envVar = `RPC_${chainId}`
14-
const url = process.env[envVar]
15-
if (!url) {
16-
throw new Error(`No RPC for chain ${chainId}: set ${envVar}`)
13+
const prefix = process.env.RPC_PREFIX_URL
14+
if (!prefix) {
15+
throw new Error(`No RPC for chain ${chainId}: set RPC_PREFIX_URL`)
1716
}
18-
return url
17+
return `${prefix.replace(/\/$/, '')}/${chainId}`
1918
}
2019

2120
/**

lib/util/chain.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,25 @@ import { ChainId } from '@uniswap/sdk-core'
33
export { ChainId }
44

55
// Each chain in SUPPORTED_CHAINS needs an RPC URL resolvable by
6-
// getRpcUrl(chainId) (Config.ts), which reads the `RPC_<chainId>` env var
7-
// (e.g. RPC_1, RPC_130, RPC_8453).
6+
// getRpcUrl(chainId) (Config.ts), which appends the chainId to
7+
// RPC_PREFIX_URL.
88
export const SUPPORTED_CHAINS = [
99
ChainId.MAINNET,
10-
ChainId.BASE,
10+
ChainId.OPTIMISM,
11+
ChainId.BNB,
1112
ChainId.UNICHAIN,
12-
ChainId.ARBITRUM_ONE,
1313
ChainId.POLYGON,
14+
ChainId.MONAD,
15+
ChainId.XLAYER,
16+
ChainId.WORLDCHAIN,
1417
ChainId.SEPOLIA,
18+
ChainId.UNICHAIN_SEPOLIA,
19+
ChainId.SONEIUM,
20+
ChainId.TEMPO,
21+
ChainId.BASE,
22+
ChainId.ARBITRUM_ONE,
23+
ChainId.CELO,
24+
ChainId.AVALANCHE,
25+
ChainId.BLAST,
26+
ChainId.ZORA,
1527
]

lib/util/constants.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ export const USE_CLASSIC_PARAMETERS = {
9595
// batchNumber and algorithmVersion are added dynamically
9696
}
9797

98-
export const RPC_HEADERS = {
98+
export const RPC_HEADERS: { [key: string]: string } = {
9999
'x-uni-service-id': 'x_order_service',
100-
} as const
100+
// Authenticate RPC requests against internal providers. The value is provided
101+
// via the RPC_HEADER_SECRET env var (sourced from Secrets Manager); omitted
102+
// when unset (e.g. local dev / unit tests).
103+
...(process.env.RPC_HEADER_SECRET ? { 'x-internal-service-secret': process.env.RPC_HEADER_SECRET } : {}),
104+
}
101105

102106
export enum TradeType {
103107
EXACT_INPUT = 'EXACT_INPUT',

test/e2e/order.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ describe('/dutch-auction/order', () => {
7979
if (!process.env.GPA_SERVICE_URL) {
8080
throw new Error('GPA_SERVICE_URL not set')
8181
}
82-
if (!process.env.RPC_1) {
83-
throw new Error('RPC_1 not set')
82+
if (!process.env.RPC_PREFIX_URL) {
83+
throw new Error('RPC_PREFIX_URL not set')
8484
}
8585
if (!process.env.TEST_WALLET_PK) {
8686
throw new Error('TEST_WALLET_PK not set')
@@ -98,7 +98,7 @@ describe('/dutch-auction/order', () => {
9898
QUOTE_API_KEY = process.env.TAPI_API_KEY
9999

100100
provider = new ethers.providers.StaticJsonRpcProvider({
101-
url: process.env.RPC_1,
101+
url: `${process.env.RPC_PREFIX_URL.replace(/\/$/, '')}/1`,
102102
headers: RPC_HEADERS
103103
})
104104
alice = new ethers.Wallet(process.env.TEST_WALLET_PK).connect(provider)

0 commit comments

Comments
 (0)