Closed Leooehh closed 1 week ago
Heya,
I've added the functionality to the mainnet-wallet
module.
It can be configured via:
modules:
## Mainnet Wallet module
mainnet-wallet:
# ...
# require minimal erc20 token balances on mainnet wallet
minErc20Balances:
- name: "WETH"
address: "0x94373a4919B3240D86eA41593D5eBa789FEF3848" # WETH9 on holesky
decimals: 18
minBalance: 10000000000000000 # 0.01 WETH
You're the goat. 🫶
I'll test it out and will close this inc asap after.
Thank you very much !
Hey!
I tried the new version but ran into a few issues, which are most likely user errors on my side T-T.
The Advanced WebServer configuration was causing the app to crash after it was accessed on port 8080. This issue stopped after I added the VPS IP or commented out corsAllowOrigin.
### Advanced WebServer config
# allow external sites embedding this faucet
corsAllowOrigin:
#- "*" # allow all - for development only!
#- "https://faucets.pk910.de"
The error after crash :
2024-06-22 21:31:32 ERROR ### Caught unhandled exception: TypeError: Cannot read properties of null (reading 'length')
Exception origin: uncaughtException
Stack Trace: TypeError: Cannot read properties of null (reading 'length')
at FaucetHttpServer.getCorsHeaders (file:///home/user/PoWFaucet/dist/webserv/FaucetHttpServer.js:130:42)
at IncomingMessage.<anonymous> (file:///home/user/PoWFaucet/dist/webserv/FaucetHttpServer.js:115:77)
at IncomingMessage.emit (node:events:519:28)
at endReadableNT (node:internal/streams/readable:1696:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
After addressing the CORS configuration by adding the server IP or commenting out the function, the faucet page does not load properly.
Here is a screenshot of the issue:
Please let me know if any further info is required, thank you very much again for your continuous help!
Oh yea, thanks for reporting :)
Yea, the explorer requires that the corsAllowOrigin
setting is either set or completely left out from the config.
Setting it without any values in the way you did makes that field "null", which causes the issue you see.
Change it to corsAllowOrigin: []
(the [] behind set the value to an empty array), or comment out the setting completely.
I'll make that more clear in the readme and check for null values :)
lol, I see I've introduced that issue myself by putting that malformed empty setting in the example config 🤦😂
0b174a8ed835bcfbfc46af7b5f91ca305f29808c should fix this on code side, so that syntax is basically allowed now
Thanks for the quick update.
I just re-tested it, and:
The CORS function is no longer causing any issues, even with the default configuration. The faucet launches without any errors shown in PoWFaucet-out.log. ✅
However, I'm still encountering the attached error in the dev view with the latest version. I tried setting up the version before the Mainnet module change, and that version works fine. This leaves me a bit confused about what might be causing the issue:
Please let me know if there is something that I can try/change on my side. Once more thanks a lot for the help < 3 I really do appreciate it.
Here's the text content of the error given in console (in-case the image doesnt load)
**Content Security Policy of your site blocks the use of 'eval' in JavaScript**
The Content Security Policy (CSP) prevents the evaluation of arbitrary strings as JavaScript to make it more difficult for an attacker to inject unauthorized code on your site.
To solve this issue, avoid using `eval()`, `new Function()`, `setTimeout([string], ...)`, and `setInterval([string], ...)` for evaluating strings.
If you absolutely must: you can enable string evaluation by adding `unsafe-eval` as an allowed source in a `script-src` directive.
⚠️ Allowing string evaluation comes at the risk of inline script injection.
| Source location | Directive | Status |
|-----------------|-------------|---------|
| script-src | blocked |
Thanks for your detailed report! Yea, I've recently introduced that bug, so thanks for catching it before the next release 😅 Should be fixed by 27c2da81e43d7c2a88bef3b7ba69040cef1a7e16, can you re-test?
The CSP warning is a bit weird, which browser are you using?
Thank you very much !
Its working all fine now, everything seems to functioning as expected.
I tried both with a chromium browser and firefox which was giving the CSP warning ( Its fully good now )
I think we can mark this as resolved for now. I'll try the ERC-20 token check function and will re-open in-case of any issues.
Once more I really appreciate your help with this, 🫶
Good morning @pk910 !
I tested the ERC20 balance check and encountered a small issue. It correctly blocks access if the user doesn't own any of the designated tokens. However, it still allows access even if the user has just "1" token, despite the minBalance being set to a much higher limit.
I used a random coin in this example, but other tokens are experiencing the same issue. I also tried changing the minBalance to different values, but that didn't help either.
Here's the config where I noticed the problem:
modules:
## Mainnet Wallet module
mainnet-wallet:
# ...
# require minimal erc20 token balances on mainnet wallet
minErc20Balances:
- name: "PEPE"
address: "0x6982508145454Ce325dDbE47a25d4ec3d2311933" # Pepe
decimals: 18
minBalance: 100000000000 # 10b
here's the message when access is successfully blocked showing 0 as requirement:
Yea, the numbers don't look right. The faucet handles balances and amounts as raw integer values (without decimals).
I guess you wanted to check for >= 10 PEPE, so given the token has 18 decimals, 10PEPE is actually a value of 10000000000000000000
(10 * 10^18
)
Silly me. That makes total sense. Everything seems to be good. Thank you very much!
Back at it againnn,
After a lot of confusion and testing on my side, I think I've figured out where the problem is.
The "[MAINNET_BALANCE_LIMIT]" error message is showing the balance of the user's wallet instead of the limit set in the config. For example, if the user owns 20 PEPE, the error message says: "You need to hold at least 20 PEPE," even if the limit is set to 50.
It's only the message that's incorrect though. The function itself is recognizing the limits just fine.
Sorry for the confusion earlier, I hope this helps.
I'll keep the ticket closed since I found a workaround by changing the alarm text manually but thought I'd let you know :)
Thanks a lot!
fixed 👍
Hey @pk910 ! I recently added a ERC-721 in addition to ERC-20 token check to the module. I tested it out and it should be functional (please lmk if you see anything wrong so I can fix it thank you very much !).
Here's the updated code in-case you wanted to add it :)
MainnetWalletModule.ts :
import Web3 from 'web3';
import { ServiceManager } from "../../common/ServiceManager.js";
import { EthWalletManager } from "../../eth/EthWalletManager.js";
import { FaucetSession } from "../../session/FaucetSession.js";
import { BaseModule } from "../BaseModule.js";
import { ModuleHookAction } from "../ModuleManager.js";
import { defaultConfig, IMainnetWalletConfig } from './MainnetWalletConfig.js';
import { FaucetError } from '../../common/FaucetError.js';
import { Erc20Abi } from '../../abi/ERC20.js';
export const Erc721Abi = [
{
"constant": true,
"inputs": [{ "name": "_owner", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "name": "balance", "type": "uint256" }],
"type": "function"
}
];
export class MainnetWalletModule extends BaseModule<IMainnetWalletConfig> {
protected readonly moduleDefaultConfig = defaultConfig;
private web3: Web3;
protected override startModule(): Promise<void> {
this.startWeb3();
this.moduleManager.addActionHook(this, ModuleHookAction.SessionStart, 7, "Mainnet Wallet check", (session: FaucetSession, userInput: any) => this.processSessionStart(session, userInput));
return Promise.resolve();
}
protected override stopModule(): Promise<void> {
return Promise.resolve();
}
private startWeb3() {
let provider = EthWalletManager.getWeb3Provider(this.moduleConfig.rpcHost);
this.web3 = new Web3(provider);
}
private async processSessionStart(session: FaucetSession, userInput: any): Promise<void> {
if (session.getSessionData<Array<string>>("skip.modules", []).indexOf(this.moduleName) !== -1)
return;
let targetAddr = session.getTargetAddr();
if (this.moduleConfig.minBalance > 0) {
let minBalance = BigInt(this.moduleConfig.minBalance);
let walletBalance: bigint;
try {
walletBalance = BigInt(await this.web3.eth.getBalance(targetAddr));
} catch (ex) {
throw new FaucetError("MAINNET_BALANCE_CHECK", "Could not get balance of mainnet wallet " + targetAddr + ": " + ex.toString());
}
if (walletBalance < minBalance)
throw new FaucetError("MAINNET_BALANCE_LIMIT", "You need to hold at least " + ServiceManager.GetService(EthWalletManager).readableAmount(minBalance, true) + " in your wallet on mainnet to use this faucet.");
}
if (this.moduleConfig.minTxCount > 0) {
let walletTxCount: bigint;
try {
walletTxCount = await this.web3.eth.getTransactionCount(targetAddr);
} catch (ex) {
throw new FaucetError("MAINNET_TXCOUNT_CHECK", "Could not get tx-count of mainnet wallet " + targetAddr + ": " + ex.toString());
}
if (walletTxCount < this.moduleConfig.minTxCount)
throw new FaucetError("MAINNET_TXCOUNT_LIMIT", "You need to submit at least " + this.moduleConfig.minTxCount + " transactions from your wallet on mainnet to use this faucet.");
}
if (this.moduleConfig.minErc20Balances.length > 0) {
let faucetAddress = ServiceManager.GetService(EthWalletManager).getFaucetAddress();
for (let i = 0; i < this.moduleConfig.minErc20Balances.length; i++) {
let erc20Token = this.moduleConfig.minErc20Balances[i];
let minBalance = BigInt(erc20Token.minBalance);
let walletBalance: bigint;
try {
let tokenContract = new this.web3.eth.Contract(Erc20Abi, erc20Token.address, {
from: faucetAddress,
});
walletBalance = BigInt(await tokenContract.methods.balanceOf(targetAddr).call());
} catch (ex) {
throw new FaucetError("MAINNET_BALANCE_CHECK", "Could not get token balance of mainnet wallet " + targetAddr + ": " + ex.toString());
}
if (walletBalance < minBalance) {
let factor = Math.pow(10, erc20Token.decimals || 18);
let amountStr = (Math.floor(parseInt(minBalance.toString()) / factor * 1000) / 1000).toString();
throw new FaucetError("MAINNET_BALANCE_LIMIT", "You need to hold at least " + amountStr + " " + erc20Token.name + " in your wallet on mainnet to use this faucet.");
}
}
}
if (this.moduleConfig.nftContract) {
let nftContractAddress = this.moduleConfig.nftContract;
let walletBalance: bigint;
try {
let nftContract = new this.web3.eth.Contract(Erc721Abi, nftContractAddress);
walletBalance = BigInt(await nftContract.methods.balanceOf(targetAddr).call());
} catch (ex) {
throw new FaucetError("MAINNET_NFT_BALANCE_CHECK", "Could not get NFT balance of mainnet wallet " + targetAddr + ": " + ex.toString());
}
if (walletBalance <= BigInt(0)) {
// Use the collection name and URL from the config
const collectionName = this.moduleConfig.nftCollectionName || "the specified collection";
const collectionUrl = this.moduleConfig.nftCollectionUrl || "#";
throw new FaucetError("MAINNET_NFT_BALANCE_LIMIT", `You need to own at least one NFT from the collection "${collectionName}". Visit ${collectionUrl} for more details.`);
}
}
}
}
MainnetWalletConfig.ts:
import { IBaseModuleConfig } from "../BaseModule.js";
export interface IMainnetWalletConfig extends IBaseModuleConfig {
rpcHost: string;
minTxCount: number;
minBalance: number;
minErc20Balances: {
name: string;
address: string;
decimals?: number;
minBalance: number;
}[];
nftContract?: string;
nftCollectionName?: string;
nftCollectionUrl?: string;
}
export const defaultConfig: IMainnetWalletConfig = {
enabled: false,
rpcHost: null,
minTxCount: 0,
minBalance: 0,
minErc20Balances: [],
nftContract: null,
nftCollectionName: null,
nftCollectionUrl: null
};
faucet-config.yaml
## Mainnet Wallet module
mainnet-wallet:
# enable / disable mainnet wallet protection
enabled: false
# RPC host for mainnet
rpcHost: "https://mainnet.infura.io/v3/YOUR-PROJECT-ID"
# require minimum balance on mainnet wallet
#minBalance: 10000000000000000 # 0.01 ETH
# require minimum number of transactions from mainnet wallet (nonce count)
minTxCount: 5
# require minimal erc20 token balances on mainnet wallet
#minErc20Balances: []
#- name: "TestToken"
# address: "0x94373a4919B3240D86eA41593D5eBa789FEF3848" # WETH9 on holesky
# decimals: 18
# minBalance: 10000000000000000 # 0.01 WETH
# require at least one NFT from the specified contract
nftContract: "0x0000000000000000000000000000000000000000"
nftCollectionName: "Name of the project"
nftCollectionUrl: "Link to the Collection"
Hey !
Would it be possible to change the Mainnet token balance check's functionality to check a specific evm token instead of the native token, or to check multiple tokens?
Thank you very much in advance!