diff --git a/engine/src/main/resources/faucet.bpmn b/engine/src/main/resources/faucet.bpmn
new file mode 100644
index 00000000..cfe18f2b
--- /dev/null
+++ b/engine/src/main/resources/faucet.bpmn
@@ -0,0 +1,175 @@
+
+
+
+
+
+ Flow_0u911t6
+
+
+
+
+
+
+
+
+
+
+
+
+ ${wallet_address}
+
+
+ Flow_0u911t6
+ Flow_16zklt4
+ Flow_0rmvjyb
+
+
+
+
+
+ ${wallet_address}
+ ${wallet_address}
+
+
+ Flow_0rmvjyb
+ Flow_1jsefni
+
+
+
+ Flow_16zklt4
+
+
+
+
+
+
+
+ ${wallet_address}
+ ${tx_hash}
+
+
+ Flow_05zefkh
+ Flow_15r1ehe
+
+
+ Flow_0n1fdm7
+
+
+
+
+ ${wallet_address}
+ ${wallet_address}
+
+
+ Flow_1jsefni
+ Flow_05zefkh
+
+
+
+ Flow_0n1fdm7
+
+
+
+
+
+
+ ${tx_hash}
+
+
+
+
+
+ Flow_15r1ehe
+ Flow_0duhv9v
+
+
+
+ Flow_0duhv9v
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/engine/src/main/resources/rsi_trading_bot.bpmn b/engine/src/main/resources/rsi_trading_bot.bpmn
index 2448d96b..ad7da2a0 100644
--- a/engine/src/main/resources/rsi_trading_bot.bpmn
+++ b/engine/src/main/resources/rsi_trading_bot.bpmn
@@ -1,6 +1,6 @@
-
-
+
+
Flow_1w72lmq
@@ -194,13 +194,7 @@ if (currentPrice > 0) {
-
-
-
-
-
-
-
+
@@ -229,10 +223,6 @@ if (currentPrice > 0) {
-
-
-
-
@@ -283,10 +273,10 @@ if (currentPrice > 0) {
-
-
-
-
+
+
+
+
@@ -366,6 +356,10 @@ if (currentPrice > 0) {
+
+
+
+
@@ -401,6 +395,12 @@ if (currentPrice > 0) {
+
+
+
+
+
+
@@ -413,10 +413,6 @@ if (currentPrice > 0) {
-
-
-
-
@@ -435,6 +431,10 @@ if (currentPrice > 0) {
+
+
+
+
diff --git a/engine/src/main/resources/sma_trading_bot.bpmn b/engine/src/main/resources/sma_trading_bot.bpmn
index 7a2cd99d..742eafaf 100644
--- a/engine/src/main/resources/sma_trading_bot.bpmn
+++ b/engine/src/main/resources/sma_trading_bot.bpmn
@@ -1,6 +1,6 @@
-
-
+
+
@@ -235,7 +235,7 @@ var sma_20 = execution.getVariable("sma_20");
-
+
@@ -245,52 +245,18 @@ var sma_20 = execution.getVariable("sma_20");
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -306,6 +272,13 @@ var sma_20 = execution.getVariable("sma_20");
+
+
+
+
+
+
+
@@ -316,47 +289,37 @@ var sma_20 = execution.getVariable("sma_20");
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -368,13 +331,10 @@ var sma_20 = execution.getVariable("sma_20");
-
-
-
-
-
-
-
+
+
+
+
@@ -385,14 +345,19 @@ var sma_20 = execution.getVariable("sma_20");
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
@@ -401,31 +366,66 @@ var sma_20 = execution.getVariable("sma_20");
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -501,10 +501,6 @@ var sma_20 = execution.getVariable("sma_20");
-
-
-
-
@@ -523,6 +519,10 @@ var sma_20 = execution.getVariable("sma_20");
+
+
+
+
diff --git a/engine/src/main/resources/trading_bot.bpmn b/engine/src/main/resources/trading_bot.bpmn
index 9fb8d045..2fc2fc36 100644
--- a/engine/src/main/resources/trading_bot.bpmn
+++ b/engine/src/main/resources/trading_bot.bpmn
@@ -1,6 +1,6 @@
-
-
+
+
@@ -112,7 +112,7 @@
-
+
@@ -126,20 +126,12 @@
-
-
-
-
-
-
-
-
@@ -149,13 +141,17 @@
+
+
+
+
-
-
-
-
+
+
+
+
@@ -195,6 +191,10 @@
+
+
+
+
diff --git a/external_workers/faucet/__init__.py b/external_workers/faucet/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/external_workers/faucet/check_delay.py b/external_workers/faucet/check_delay.py
new file mode 100644
index 00000000..db7df4bc
--- /dev/null
+++ b/external_workers/faucet/check_delay.py
@@ -0,0 +1,32 @@
+import os
+
+from camunda.external_task.external_task import ExternalTask, TaskResult
+from camunda.external_task.external_task_worker import ExternalTaskWorker
+from redis import Redis
+
+from external_workers.faucet.config import DEFAULT_CONFIG, CAMUNDA_URL
+
+REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
+REDIS_PORT = os.getenv("REDIS_PORT", 6379)
+DELAY_TOPIC_NAME = "check_delay"
+
+
+def check_delay(task: ExternalTask) -> TaskResult:
+ variables = task.get_variables()
+ wallet_address = variables.get("wallet_address")
+ if redis_client.exists(wallet_address):
+ return task.bpmn_error(429, "DELAY")
+ return task.complete(variables)
+
+
+if __name__ == "__main__":
+ redis_client = Redis(
+ host=REDIS_HOST,
+ port=REDIS_PORT,
+ decode_responses=True,
+ )
+ worker = ExternalTaskWorker(
+ worker_id="validate_wallet_address",
+ base_url=CAMUNDA_URL,
+ config=DEFAULT_CONFIG,
+ ).subscribe([DELAY_TOPIC_NAME], check_delay)
diff --git a/external_workers/faucet/config.py b/external_workers/faucet/config.py
new file mode 100644
index 00000000..ad09590b
--- /dev/null
+++ b/external_workers/faucet/config.py
@@ -0,0 +1,15 @@
+import os
+
+DEFAULT_CONFIG = {
+ "maxTasks": 1,
+ "lockDuration": 10000,
+ "asyncResponseTimeout": 5000,
+ "retries": 3,
+ "retryTimeout": 15000,
+ "sleepSeconds": 30,
+}
+DELAY_TOPIC_NAME = os.getenv("DELAY_TOPIC_NAME", "check_delay")
+CAMUNDA_URL = os.getenv("CAMUNDA_URL", "http://localhost:8080/engine-rest")
+PRIVATE_KEY = os.getenv(
+ "PRIVATE_KEY", "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
+)
diff --git a/external_workers/faucet/send_tokens.py b/external_workers/faucet/send_tokens.py
new file mode 100644
index 00000000..b2eb78ef
--- /dev/null
+++ b/external_workers/faucet/send_tokens.py
@@ -0,0 +1,52 @@
+import os
+
+from camunda.external_task.external_task import ExternalTask, TaskResult
+from camunda.external_task.external_task_worker import ExternalTaskWorker
+from redis import Redis
+from web3 import Web3
+
+from external_workers.faucet.config import DEFAULT_CONFIG, PRIVATE_KEY
+from external_workers.web3_workers.check_transaction_status import WEB3_URL
+
+REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
+REDIS_PORT = os.getenv("REDIS_PORT", 6379)
+
+SEND_TOKENS_TOPIC_NAME = os.getenv("SEND_TOKENS_TOPIC_NAME", "send_tokens")
+
+if not PRIVATE_KEY:
+ raise ValueError("PRIVATE_KEY is not set")
+w3 = Web3(Web3.HTTPProvider(WEB3_URL))
+account = w3.eth.account.from_key(PRIVATE_KEY)
+CHAIN_ID = w3.eth.chain_id
+GWEI = 10**9
+
+
+def handle_send_native_tokens(task: ExternalTask) -> TaskResult:
+ variables = task.get_variables()
+ wallet_address = variables.get("wallet_address")
+ nonce = w3.eth.get_transaction_count(account.address)
+ tx = {
+ "to": wallet_address,
+ "value": w3.to_wei(1, "ether"),
+ "gas": 21000,
+ "gasPrice": w3.eth.gas_price + 3 * GWEI,
+ "nonce": nonce,
+ "chainId": CHAIN_ID,
+ }
+ signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
+ tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
+ redis_client.set(wallet_address, tx_hash, ex=86400) # 24h
+ return task.complete({"tx_hash": tx_hash})
+
+
+if __name__ == "__main__":
+ redis_client = Redis(
+ host=REDIS_HOST,
+ port=REDIS_PORT,
+ decode_responses=True,
+ )
+ worker = ExternalTaskWorker(
+ worker_id="validate_wallet_address",
+ base_url=CAMUNDA_URL,
+ config=DEFAULT_CONFIG,
+ ).subscribe([SEND_TOKENS_TOPIC_NAME], handle_send_native_tokens)
diff --git a/external_workers/faucet/validate_wallet.py b/external_workers/faucet/validate_wallet.py
new file mode 100644
index 00000000..59d80660
--- /dev/null
+++ b/external_workers/faucet/validate_wallet.py
@@ -0,0 +1,43 @@
+import os
+
+from camunda.external_task.external_task import ExternalTask, TaskResult
+from camunda.external_task.external_task_worker import ExternalTaskWorker
+from web3 import Web3
+from redis import Redis
+
+from external_workers.faucet.config import DEFAULT_CONFIG, CAMUNDA_URL
+from external_workers.faucet.send_tokens import WEB3_URL
+
+REDIS_HOST = os.getenv("REDIS_HOST", "localhost")
+REDIS_PORT = os.getenv("REDIS_PORT", 6379)
+
+VALIDATE_TOPIC_NAME = os.getenv("TOPIC_NAME", "validate_wallet_address")
+w3 = Web3(Web3.HTTPProvider(WEB3_URL))
+
+
+def handle_validate_task(task: ExternalTask) -> TaskResult:
+ variables = task.get_variables()
+ wallet_address = variables.get("wallet_address")
+ if not is_valid_wallet_address(wallet_address):
+ return task.bpmn_error(429, "INVALID_WALLET_ADDRESS")
+ variables["wallet_address"] = w3.to_checksum_address(wallet_address)
+ return task.complete(variables)
+
+
+def is_valid_wallet_address(wallet_address: str) -> bool:
+ if not w3.is_address(wallet_address):
+ return False
+ return True
+
+
+if __name__ == "__main__":
+ redis_client = Redis(
+ host=REDIS_HOST,
+ port=REDIS_PORT,
+ decode_responses=True,
+ )
+ worker = ExternalTaskWorker(
+ worker_id="validate_wallet_address",
+ base_url=CAMUNDA_URL,
+ config=DEFAULT_CONFIG,
+ ).subscribe([VALIDATE_TOPIC_NAME], handle_validate_task)
diff --git a/external_workers/requirements.txt b/external_workers/requirements.txt
index a2868c7b..68a76007 100644
--- a/external_workers/requirements.txt
+++ b/external_workers/requirements.txt
@@ -2,3 +2,4 @@ camunda-external-task-client-python3==4.4.0
pydantic==2.7.0
web3==6.17.2
ta==0.11.0
+redis==3.5.3
\ No newline at end of file