Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8230d5b
fix: remove dead Multichain BNB from Polygon bluechip tokens
munikes Aug 6, 2026
b1a7584
fix: correct WBNB symbol label on BSC (was mislabeled as WETH)
munikes Aug 6, 2026
8179003
fix: batch CoinGecko token price requests instead of one-per-token
munikes Aug 6, 2026
3f1f418
fix: remove FEI and RENBTC from Ethereum default tokens
munikes Aug 6, 2026
f144e1c
Merge remote-tracking branch 'origin/fix/bsc-wbnb-mislabeled'
munikes Aug 6, 2026
bf153a4
Merge remote-tracking branch 'origin/fix/coingecko-batch-requests'
munikes Aug 6, 2026
4f27a06
oMerge remote-tracking branch 'origin/fix/ethereum-fei-renbtc-defunct'
munikes Aug 6, 2026
b90e6b3
fix: remove leftover REN_TOKENS_ETH import (broke build)
munikes Aug 6, 2026
b3dac5c
fix: publish/getMessages ignore config.waku.pubSubTopic, hardcoded to…
munikes Aug 20, 2026
dcce430
fix: nwaku default config points at dead cluster 0, with stale bootst…
munikes Aug 20, 2026
b1bb55d
docs: warn that priceTTLInMS must stay >= CoinGecko refreshDelayInMS
munikes Aug 20, 2026
6e9b432
docs: warn that profitMargin overrides don't retroactively update bui…
munikes Aug 21, 2026
1127150
Merge branch 'fix/hardcoded-pubsub-topic'
munikes Aug 21, 2026
ca83715
Merge branch 'fix/nwaku-cluster-migration'
munikes Aug 21, 2026
f17ad4f
Merge branch 'docs/price-ttl-refresh-delay-gotcha'
munikes Aug 21, 2026
5e40637
Merge branch 'docs/profitmargin-frozen-fees-gotcha'
munikes Aug 21, 2026
7be221e
feat: upgrade @railgun-community/wallet to 10.9.0, handle new EIP-770…
munikes Aug 25, 2026
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
12 changes: 9 additions & 3 deletions docker/nwaku/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,27 @@ keep-alive=false
staticnode=[
# dev-node
"/dns4/core.rootedinprivacy.com/tcp/60000/p2p/16Uiu2HAm4Ai1GzKv4EykU26ST1BPT4AHtABsYCLKrDG74GWX7D6H",
"/dns4/relay-a.rootedinprivacy.com/tcp/8000/wss/p2p/16Uiu2HAmMkCL9Y4R6V8eyTfHRfPA9JUBSnsJXwiUvgUJHU7N9AsR",
"/dns4/relay-a.rootedinprivacy.com/tcp/30304/p2p/16Uiu2HAmMkCL9Y4R6V8eyTfHRfPA9JUBSnsJXwiUvgUJHU7N9AsR",
"/dns4/relay-b.rootedinprivacy.com/tcp/8000/wss/p2p/16Uiu2HAm1XAZUTsZcbvLBWe9MR2QxEJ1oowv5VgZavwiwTEx4uPC",
"/dns4/relay-b.rootedinprivacy.com/tcp/30304/p2p/16Uiu2HAm1XAZUTsZcbvLBWe9MR2QxEJ1oowv5VgZavwiwTEx4uPC",
"/dns4/client-edge.rootedinprivacy.com/tcp/8000/wss/p2p/16Uiu2HAmS8YbNhE1rJGBQ3VaubZjM1iAR7zVvtLf6CrEB3Hc9Krr",
"/dns4/client-edge.rootedinprivacy.com/tcp/30304/p2p/16Uiu2HAmS8YbNhE1rJGBQ3VaubZjM1iAR7zVvtLf6CrEB3Hc9Krr",
]
dns-discovery=false
discv5-discovery=false
discv5-bootstrap-node=[
# dev-node
"enr:-Ou4QF5uFxGUG9WVUuysEWm8nketT_Pe6hG0P6NRxu9dTOwHWHWQgGFhm-vqmYwUvU0LtrW2LhSEO7iEHKD4l8JEd14BgmlkgnY0gmlwhIxSIGiKbXVsdGlhZGRyc7hAAB02GGNvcmUucm9vdGVkaW5wcml2YWN5LmNvbQbqYAAfNhhjb3JlLnJvb3RlZGlucHJpdmFjeS5jb20GH0DeA4Jyc48AAAYAAAABAAIAAwAEAAWJc2VjcDI1NmsxoQKB48KxVciQEh-6cSmBBGdTm-ehZ_6_oht-pjmrU52YEoN0Y3CC6mCFd2FrdTIN",
]
shard=[0, 1, 2, 3, 4, 5]
shard=[0, 1, 2, 3, 4, 5, 6, 7]
max-msg-size="512KiB"

cluster-id=0 # needed when switch to 0.33 nwaku, pubsub-topic is deprecated
cluster-id=5 # needed when switch to 0.33 nwaku, pubsub-topic is deprecated
filter-subscription-timeout=3000
filter-max-peers-to-serve=10000
reliability=false
num-shards-in-network=6
num-shards-in-network=8

peer-store-capacity=500
max-connections=500
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"dependencies": {
"@0x/contract-addresses": "^8.0.0",
"@noble/ed25519": "^1.7.1",
"@railgun-community/shared-models": "^8.0.0",
"@railgun-community/wallet": "^10.8.1",
"@railgun-community/shared-models": "^8.0.1",
"@railgun-community/wallet": "^10.9.0",
"@walletconnect/jsonrpc-types": "^1.0.2",
"@walletconnect/jsonrpc-utils": "^1.0.4",
"axios": "1.7.2",
Expand Down
6 changes: 6 additions & 0 deletions src/server/api/block-native/gas-by-speed-block-native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ export const getGasDetailsBySpeedBlockNative = async (
},
};
}
case EVMGasType.Type4: {
// EIP-7702 not yet supported by this broadcaster.
throw new Error(
'EVMGasType 4 (EIP-7702) not yet supported for gas-by-speed lookups.',
);
}
}
} catch (err) {
dbg(err);
Expand Down
36 changes: 14 additions & 22 deletions src/server/api/coingecko/coingecko-price.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,20 @@ const coingeckoPriceLookup = async (
): Promise<TokenAddressPrice[]> => {
const currency = 'usd';

const paramArr = tokenAddresses.map((address) => {
return {
contract_addresses: address,
vs_currencies: currency,
include_last_updated_at: true,
};
});
let coingeckoPriceMap: CoingeckoPriceMap = {};
for (const params of paramArr) {
try {
// eslint-disable-next-line no-await-in-loop
const geckoPriceMap: CoingeckoPriceMap = await getCoingeckoData(
CoingeckoApiEndpoint.PriceLookup,
coingeckoNetworkId,
params,
);
coingeckoPriceMap = { ...coingeckoPriceMap, ...geckoPriceMap };
// eslint-disable-next-line no-await-in-loop
await delay(250);
} catch (error) {
console.error(error);
}
try {
const geckoPriceMap: CoingeckoPriceMap = await getCoingeckoData(
CoingeckoApiEndpoint.PriceLookup,
coingeckoNetworkId,
{
contract_addresses: tokenAddresses.join(','),
vs_currencies: currency,
include_last_updated_at: true,
},
);
coingeckoPriceMap = { ...coingeckoPriceMap, ...geckoPriceMap };
} catch (error) {
console.error(error);
}

const tokenPrices = tokenPriceArrayFromCoingeckoPriceMap(
Expand All @@ -77,7 +69,7 @@ export const coingeckoUpdatePricesByAddresses = async (
tokenAddresses: string[],
updater: TokenPriceUpdater,
): Promise<void> => {
const batchSize = 50;
const batchSize = 30; // CoinGecko Demo API tier limit is 30 addresses per request
for (let i = 0; i < tokenAddresses.length; i += batchSize) {
const batch = tokenAddresses.slice(i, i + batchSize);
// eslint-disable-next-line no-await-in-loop
Expand Down
10 changes: 10 additions & 0 deletions src/server/config/config-fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ const feeConfig = (
profit,
});

// IMPORTANT: `profit` defaults to configDefaults.transactionFees.profitMargin,
// but a default parameter is resolved the moment this function is CALLED,
// not read live afterwards. Default network fee configs are built once, at
// module load time in config-networks.ts, using whatever profitMargin holds
// at that point (the built-in default). If you override profitMargin later
// in MY-CONFIG.ts, the fee config already built for a network is NOT
// retroactively updated - setting profitMargin alone silently does nothing
// to fees computed before the override ran. To actually apply a new margin,
// call setFeesForNetworkEVM(chain, feeConfigL1(ratio)) (or feeConfigL2)
// again, AFTER setting profitMargin, so it re-reads the current value.
export const feeConfigL1 = (
gasEstimateLimitToActualRatio: number,
gasEstimateVarianceBuffer = 0.1,
Expand Down
9 changes: 9 additions & 0 deletions src/server/config/config-networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ const MINIMUM_BALANCE_FOR_AVAILABILITY_L2 = 0.01;

// 10 minute timeout on ticket prices retrieved from API.
// Shorter is safer, but Coingecko free tier can lag by 15-30 minutes.
//
// IMPORTANT: this MUST stay >= the price refresher's refreshDelayInMS
// (configTokenPriceRefresher.tokenPriceRefreshers[...].refreshDelayInMS,
// overridable in MY-CONFIG.ts). If you raise the refresh interval (e.g. to
// save CoinGecko free-tier quota) without raising this TTL by at least as
// much, every cycle gets a guaranteed window - equal to the gap between the
// two values - where cached prices are considered "expired" and the
// broadcaster silently stops publishing fees for every token (NO TOKEN
// PRICE warnings), with no underlying error: the API call itself succeeds.
const defaultTokenPriceTTL = 10 * 60 * 1000;

const networksConfig: NetworksConfig = {
Expand Down
14 changes: 11 additions & 3 deletions src/server/config/config-token-addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ export enum TokenAddressEthereum {
USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
RAIL = '0xe76C6c83af64e4C60245D8C7dE953DF673a7A33D',
FRAX = '0x853d955acef822db058eb8505911ed77f175b99e',
FEI = '0x956F47F50A910163D8BF957Cf5846D573E7f87CA',
RAI = '0x03ab458634910aad20ef5f1c8ee96f1d6ac54919',
RENBTC = '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d',
// Unused: Fei Protocol dissolved Aug 2022 (Tribe DAO voted to redeem
// FEI->DAI and wind down). CoinGecko price consistently fails to
// refresh in production (observed stuck >25min while other tokens
// refreshed normally every cycle).
// FEI = '0x956F47F50A910163D8BF957Cf5846D573E7f87CA',
// Unused: backing bridge (RenVM) shut down in late 2022 after the
// Alameda/FTX collapse. No active development since; ~$3/24h volume
// on-chain as of 2026. Not viable as a fee token.
// RENBTC = '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d',
}
export enum TokenAddressGoerli {
WETH = '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6',
Expand All @@ -28,7 +35,8 @@ export enum TokenAddressBSC {
export enum TokenAddressPolygonPOS {
WMATIC = '0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270',
DAI = '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063',
BNB = '0xA649325Aa7C5093d12D6F98EB4378deAe68CE23F',
// Unused: bridged BNB via Multichain, bridge defunct since 2023 hack. $0 liquidity/price on-chain.
// BNB = '0xA649325Aa7C5093d12D6F98EB4378deAe68CE23F',
WETH = '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619',
USDC = '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
USDT = '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
Expand Down
14 changes: 1 addition & 13 deletions src/server/config/config-token-sets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ export const STABLES_ETH: AddressToTokenMap = {
[TokenAddressEthereum.FRAX]: {
symbol: 'FRAX',
},
[TokenAddressEthereum.FEI]: {
symbol: 'FEI',
},
[TokenAddressEthereum.RAI]: {
symbol: 'RAI',
},
Expand All @@ -39,15 +36,9 @@ export const BLUECHIP_ETH: AddressToTokenMap = {
},
};

export const REN_TOKENS_ETH: AddressToTokenMap = {
[TokenAddressEthereum.RENBTC]: {
symbol: 'RENBTC',
},
};

export const BLUECHIP_BSC: AddressToTokenMap = {
[TokenAddressBSC.WBNB]: {
symbol: 'WETH',
symbol: 'WBNB',
},

[TokenAddressBSC.CAKE]: {
Expand All @@ -58,9 +49,6 @@ export const BLUECHIP_POLY: AddressToTokenMap = {
[TokenAddressPolygonPOS.WETH]: {
symbol: 'WETH',
},
[TokenAddressPolygonPOS.BNB]: {
symbol: 'WETH',
},
[TokenAddressPolygonPOS.WMATIC]: {
symbol: 'WMATIC',
},
Expand Down
2 changes: 0 additions & 2 deletions src/server/config/config-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
STABLES_BSC,
STABLES_POLY,
BLUECHIP_ETH,
REN_TOKENS_ETH,
BLUECHIP_BSC,
BLUECHIP_POLY,
BLUECHIP_ARBITRUM,
Expand All @@ -36,7 +35,6 @@ const tokensConfig: NetworkTokensConfig = {
[NetworkChainID.Ethereum]: {
...STABLES_ETH,
...BLUECHIP_ETH,
...REN_TOKENS_ETH,
},
[NetworkChainID.EthereumGoerli]: {
[TokenAddressGoerli.WETH]: {
Expand Down
6 changes: 6 additions & 0 deletions src/server/fees/gas-by-speed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ export const getGasDetailsForSpeed = async (
);
return gasDetailsBySpeed[percentile];
}
case EVMGasType.Type4: {
// EIP-7702 not yet supported by this broadcaster.
throw new Error(
'EVMGasType 4 (EIP-7702) not yet supported for gas-by-speed lookups.',
);
}
}
};

Expand Down
5 changes: 5 additions & 0 deletions src/server/fees/gas-estimate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const getEstimateGasDetailsRelayed = async (
// minGasPrice not allowed on EVMGasType 2
throw new Error('EVMGasType 2 not allowed for Broadcaster transactions.');
}
if (evmGasType === EVMGasType.Type4) {
// EIP-7702 (Type4) transactions use maxFeePerGas/maxPriorityFeePerGas,
// not gasPrice - not yet supported on this relayed estimation path.
throw new Error('EVMGasType 4 (EIP-7702) not yet supported for Broadcaster transactions.');
}
const gasPrice = minGasPrice;
const transactionWithOptionalMinGas: ContractTransaction = {
...transaction,
Expand Down
20 changes: 10 additions & 10 deletions src/server/networking/waku-rest-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ export type WakuApiClientOptions = {
export enum WakuRequestMethods {
DebugInfo = '/debug/v1/info', // GET
PublishSubscription = '/relay/v1/subscriptions', // POST
PublishMessage = '/relay/v1/messages/%2Fwaku%2F2%2Frs%2F0%2F1', // POST - requires pubsub topic UTF8 URL encoded /waku/2/rs/0/1
GetMessages = '/relay/v1/messages/%2Fwaku%2F2%2Frs%2F0%2F1', // GET - requires pubsub topic UTF8 URL encoded /waku/2/rs/0/1
DeleteSubscriptions = '/relay/v1/subscriptions', // DELETE
}

const relayMessagesPath = (topic: string): string =>
`/relay/v1/messages/${encodeURIComponent(topic)}`;

const MAX_RETRIES = 4;

const checkResponseStatus = (response: any, dbg: any) => {
Expand Down Expand Up @@ -76,12 +77,10 @@ export class WakuRestApiClient {

static determineRequestType(method: string) {
switch (method) {
case WakuRequestMethods.DebugInfo:
case WakuRequestMethods.GetMessages: {
case WakuRequestMethods.DebugInfo: {
return 'GET';
}
case WakuRequestMethods.PublishSubscription:
case WakuRequestMethods.PublishMessage: {
case WakuRequestMethods.PublishSubscription: {
return 'POST';
}
case WakuRequestMethods.DeleteSubscriptions: {
Expand Down Expand Up @@ -175,18 +174,19 @@ export class WakuRestApiClient {
const { timestamp } = message;
const payload = Buffer.from(message.payload).toString('base64');
const { contentTopic } = message;
const publishPath = relayMessagesPath(topic);
if (contentTopic?.includes('fees') === true) {
// we have fee message.. dont try to resend.
const data = await this.request(
WakuRequestMethods.PublishMessage,
publishPath,
'POST',
{ payload, timestamp, version: 0, contentTopic },
MAX_RETRIES,
);
return data;
}

const data = await this.request(WakuRequestMethods.PublishMessage, 'POST', {
const data = await this.request(publishPath, 'POST', {
payload,
timestamp,
version: 0,
Expand All @@ -212,10 +212,10 @@ export class WakuRestApiClient {
* however, specifying contentTopics locally filters out uninteresting messages before return
*/
async getMessages(
topic: string, // unused
topic: string,
contentTopics: string[] = [],
): Promise<WakuRelayMessage[]> {
const data = await this.request(WakuRequestMethods.GetMessages, 'GET', []);
const data = await this.request(relayMessagesPath(topic), 'GET', []);

if (isDefined(data.error)) {
throw data.error;
Expand Down
6 changes: 6 additions & 0 deletions src/server/transactions/process-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ export const processTransaction = async (
if (evmGasType === EVMGasType.Type2) {
throw new Error('Invalid gas type for Broadcaster transaction.');
}
if (evmGasType === EVMGasType.Type4) {
// EIP-7702 requests are rejected earlier, in transact-method.ts, before
// reaching this point - this is a defensive guard, not expected in
// practice today.
throw new Error('EVMGasType 4 (EIP-7702) not yet supported for Broadcaster transactions.');
}

const transactionRequestForGasEstimate: ContractTransaction = {
...transaction,
Expand Down
12 changes: 12 additions & 0 deletions src/server/waku-broadcaster/methods/transact-method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import configNetworks from '../../config/config-networks';
import {
BroadcasterEncryptedMethodParams,
BroadcasterRawParamsTransact,
BroadcasterTransactRequestType,
TXIDVersion,
isDefined,
versionCompare,
Expand Down Expand Up @@ -98,6 +99,17 @@ export const transactMethod = async (
return undefined;
}

// EIP-7702 (TX7702) requests are not yet supported by this broadcaster.
// Reject explicitly instead of assuming the old COMMON-only shape - the
// fields below (to/data/minGasPrice/useRelayAdapt/etc.) only exist on the
// COMMON variant.
if (decrypted.transactType !== BroadcasterTransactRequestType.COMMON) {
dbg('Cannot process tx - TX7702 (EIP-7702) requests not yet supported');
// Do nothing. No error response.
await incrementReliability(incomingChain, ReliabilityMetric.BAD_DATA);
return undefined;
}

const {
chainType,
chainID,
Expand Down
Loading