-
-
Notifications
You must be signed in to change notification settings - Fork 17
Feat/smart contracts #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e232e2c
8e80004
1998d91
8423405
0812e46
e556935
116a09b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # Counter Smart Contract Example | ||
| # | ||
| # This is a simple counter contract designed to demonstrate the basic | ||
| # structure of smart contracts in MiniChain. | ||
| # | ||
| # Available built-ins in the MiniChain Sandbox: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In addition to documenting this here, could you also document the available built-ins in a more suitable location (e.g. in the place where these built-ins are being made available)? |
||
| # - `storage`: A dictionary persisting state across executions. | ||
| # - `msg`: A dictionary containing transaction info: | ||
| # - `msg['sender']` : The address of the caller. | ||
| # - `msg['value']` : The amount of coins attached to the call. | ||
| # - `msg['data']` : The payload string. | ||
| # | ||
| # Available functions: range(), len(), min(), max(), abs(), str(), bool(), float(), int(), list(), dict(), tuple(), sum() | ||
| # | ||
| # NOTE: The sandbox does NOT allow imports, print(), or any double-underscore methods. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you also document this in the place where this is being enforced? |
||
|
|
||
| if msg['data'] == 'increment': | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting! this is good enough for MiniChain. For a production-ready chain, it would be interesting to find a way to allow contracts devs to write non-embedded python code, such as: Instead of having to use the msg and storage built-ins explicitly. Let's discuss our options in discord. |
||
| # Retrieve the current counter value, defaulting to 0 if it doesn't exist | ||
| current_value = storage.get('counter', 0) | ||
|
|
||
| # Increment the counter | ||
| storage['counter'] = current_value + 1 | ||
|
|
||
| elif msg['data'] == 'decrement': | ||
| current_value = storage.get('counter', 0) | ||
| storage['counter'] = current_value - 1 | ||
|
|
||
| elif msg['data'] == 'reset': | ||
| # You can restrict who can reset the counter by checking the sender! | ||
| # (Just an example, anyone can call this one) | ||
| storage['counter'] = 0 | ||
|
|
||
| else: | ||
| # If the payload doesn't match any known command, raise an exception. | ||
| # This will fail the transaction and refund the 'amount' to the sender, | ||
| # but the network will keep the 'fee' as gas. | ||
| raise Exception("Unknown command. Valid commands: increment, decrement, reset") | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,90 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # MiniSwap (DEX) Smart Contract Example | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # This contract implements a minimal Automated Market Maker (AMM) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # using the x * y = k constant product formula. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # It trades the native MiniChain coin (msg['value']) against a minted DEX Token. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Valid Payloads: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # - 'init' (Must send initial native coins to provide liquidity) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # - 'buy' (Sends native coins, receives DEX tokens) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # - 'sell:<amount>' (Sells DEX tokens, receives native coins) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Note: Since native coins sent to the contract are automatically added to the | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # contract's balance by the state manager BEFORE execution, msg['value'] is already | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # inside the contract's physical balance. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if msg['data'] == 'init': | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Initialize the liquidity pool | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if storage.get('k') is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Already initialized") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if msg['value'] <= 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Must provide initial native coin liquidity") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # We will arbitrarily mint 1000 DEX tokens to match the initial coin liquidity | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['native_reserve'] = msg['value'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['token_reserve'] = 1000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['k'] = storage['native_reserve'] * storage['token_reserve'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Give the initial tokens to the creator | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage[msg['sender']] = 1000 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif msg['data'] == 'buy': | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # User sends native coins to buy DEX tokens | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if storage.get('k') is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Not initialized") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if msg['value'] <= 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Must send coins to buy tokens") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Calculate how many tokens to give using x * y = k | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (native_reserve + msg['value']) * (token_reserve - tokens_out) = k | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_native_reserve = storage['native_reserve'] + msg['value'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_token_reserve = storage['k'] // new_native_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| tokens_out = storage['token_reserve'] - new_token_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if tokens_out <= 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Not enough tokens to dispense") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Update reserves | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['native_reserve'] = new_native_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['token_reserve'] = new_token_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Credit tokens to buyer | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sender = msg['sender'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage[sender] = storage.get(sender, 0) + tokens_out | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| elif msg['data'].startswith('sell:'): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # User sells DEX tokens to get native coins back | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if storage.get('k') is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Not initialized") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| parts = msg['data'].split(':') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| tokens_to_sell = int(parts[1]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| sender = msg['sender'] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| sender_tokens = storage.get(sender, 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if sender_tokens < tokens_to_sell: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Insufficient token balance") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Deduct tokens from user | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage[sender] -= tokens_to_sell | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+60
to
+70
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Validate
Suggested fix parts = msg['data'].split(':')
+ if len(parts) != 2:
+ raise Exception("Invalid sell format")
tokens_to_sell = int(parts[1])
+ if tokens_to_sell <= 0:
+ raise Exception("Amount must be positive")
sender = msg['sender']
sender_tokens = storage.get(sender, 0)📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.15.15)[error] 60-60: Undefined name (F821) [error] 63-63: Undefined name (F821) [error] 64-64: Undefined name (F821) [warning] 66-66: Create your own exception (TRY002) [warning] 66-66: Avoid specifying long messages outside the exception class (TRY003) [error] 69-69: Undefined name (F821) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Calculate how many native coins to give using x * y = k | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # (token_reserve + tokens_to_sell) * (native_reserve - coins_out) = k | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_token_reserve = storage['token_reserve'] + tokens_to_sell | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| new_native_reserve = storage['k'] // new_token_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| coins_out = storage['native_reserve'] - new_native_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if coins_out <= 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Not enough coins to dispense") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Update reserves | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['native_reserve'] = new_native_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage['token_reserve'] = new_token_reserve | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Wait! In MiniChain, smart contracts cannot arbitrarily initiate outgoing transactions yet. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add an issue in our issue tracker for this? |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # To properly implement 'sell', the contract engine would need a 'transfer_out' API. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # For now, we will just record their native coin balance in storage. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| storage[f"{sender}_native_credit"] = storage.get(f"{sender}_native_credit", 0) + coins_out | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise Exception("Unknown command.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # Stablecoin (ERC-20 style) Smart Contract Example | ||
| # | ||
| # This contract implements a minimal fungible token. | ||
| # | ||
| # Valid Payloads: | ||
| # - 'mint:<amount>' | ||
| # - 'transfer:<recipient_address>:<amount>' | ||
|
|
||
| if msg['data'].startswith('mint:'): | ||
| # In a real contract, you would restrict this to an owner address! | ||
| # For this example, anyone can mint tokens to themselves. | ||
| amount = int(msg['data'].split(':')[1]) | ||
| if amount <= 0: | ||
| raise Exception("Amount must be positive") | ||
|
|
||
| sender = msg['sender'] | ||
| storage[sender] = storage.get(sender, 0) + amount | ||
| storage['total_supply'] = storage.get('total_supply', 0) + amount | ||
|
|
||
| elif msg['data'].startswith('transfer:'): | ||
| parts = msg['data'].split(':') | ||
| if len(parts) != 3: | ||
| raise Exception("Invalid transfer format") | ||
|
|
||
| to_address = parts[1] | ||
| amount = int(parts[2]) | ||
|
|
||
| if amount <= 0: | ||
| raise Exception("Amount must be positive") | ||
|
|
||
| sender = msg['sender'] | ||
| sender_balance = storage.get(sender, 0) | ||
|
|
||
| if sender_balance >= amount: | ||
| storage[sender] -= amount | ||
| storage[to_address] = storage.get(to_address, 0) + amount | ||
| else: | ||
| raise Exception("Insufficient token balance") | ||
|
|
||
| else: | ||
| raise Exception("Unknown command. Valid commands: mint:<amount>, transfer:<to>:<amount>") |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -27,11 +27,11 @@ | |||||||||||||||||||
|
|
||||||||||||||||||||
| from minichain import Transaction, Blockchain, Block, State, Mempool, P2PNetwork, mine_block | ||||||||||||||||||||
| from minichain.validators import is_valid_receiver | ||||||||||||||||||||
| from minichain.block import calculate_receipt_root | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| logger = logging.getLogger(__name__) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| BURN_ADDRESS = "0" * 40 | ||||||||||||||||||||
| TRUSTED_PEERS = set() | ||||||||||||||||||||
| LOCALHOST_PEERS = {"127.0.0.1", "::1", "localhost", "0:0:0:0:0:0:0:1"} | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -61,13 +61,17 @@ def mine_and_process_block(chain, mempool, miner_pk): | |||||||||||||||||||
| temp_state = chain.state.copy() | ||||||||||||||||||||
| mineable_txs = [] | ||||||||||||||||||||
| stale_txs = [] | ||||||||||||||||||||
| receipts = [] | ||||||||||||||||||||
| for tx in pending_txs: | ||||||||||||||||||||
| expected_nonce = temp_state.get_account(tx.sender).get("nonce", 0) | ||||||||||||||||||||
| if tx.nonce < expected_nonce: | ||||||||||||||||||||
| stale_txs.append(tx) | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| if temp_state.validate_and_apply(tx): | ||||||||||||||||||||
|
|
||||||||||||||||||||
| receipt = temp_state.validate_and_apply(tx) | ||||||||||||||||||||
| if receipt is not None: | ||||||||||||||||||||
| mineable_txs.append(tx) | ||||||||||||||||||||
| receipts.append(receipt) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if stale_txs: | ||||||||||||||||||||
| mempool.remove_transactions(stale_txs) | ||||||||||||||||||||
|
|
@@ -76,12 +80,16 @@ def mine_and_process_block(chain, mempool, miner_pk): | |||||||||||||||||||
| logger.info("No mineable transactions in current queue window.") | ||||||||||||||||||||
| return None | ||||||||||||||||||||
|
|
||||||||||||||||||||
| temp_state.credit_mining_reward(miner_pk) | ||||||||||||||||||||
| total_fees = sum(getattr(r, 'gas_used', 0) for r in receipts) | ||||||||||||||||||||
| temp_state.credit_mining_reward(miner_pk, reward=temp_state.DEFAULT_MINING_REWARD + total_fees) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| block = Block( | ||||||||||||||||||||
| index=chain.last_block.index + 1, | ||||||||||||||||||||
| previous_hash=chain.last_block.hash, | ||||||||||||||||||||
| transactions=mineable_txs, | ||||||||||||||||||||
| state_root=temp_state.state_root(), | ||||||||||||||||||||
| receipt_root=calculate_receipt_root(receipts), | ||||||||||||||||||||
| receipts=receipts, | ||||||||||||||||||||
| miner=miner_pk, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -201,28 +209,33 @@ async def cli_loop(sk, pk, chain, mempool, network): | |||||||||||||||||||
| print(" (no accounts yet)") | ||||||||||||||||||||
| for addr, acc in accounts.items(): | ||||||||||||||||||||
| tag = " (you)" if addr == pk else "" | ||||||||||||||||||||
| print(f" {addr[:12]}... balance={acc['balance']} nonce={acc['nonce']}{tag}") | ||||||||||||||||||||
| contract_tag = " [Contract]" if acc.get("code") else "" | ||||||||||||||||||||
| print(f" {addr[:12]}... balance={acc['balance']} nonce={acc['nonce']}{tag}{contract_tag}") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # ── send ── | ||||||||||||||||||||
| elif cmd == "send": | ||||||||||||||||||||
| if len(parts) < 3: | ||||||||||||||||||||
| print(" Usage: send <receiver_address> <amount>") | ||||||||||||||||||||
| print(" Usage: send <receiver_address> <amount> [fee]") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| receiver = parts[1] | ||||||||||||||||||||
| if not is_valid_receiver(receiver): | ||||||||||||||||||||
| print(" Invalid receiver format. Expected 40 or 64 hex characters.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| amount = int(parts[2]) | ||||||||||||||||||||
| fee = int(parts[3]) if len(parts) > 3 else 0 | ||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||
| print(" Amount must be an integer.") | ||||||||||||||||||||
| print(" Amount and fee must be integers.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| if amount <= 0: | ||||||||||||||||||||
| print(" Amount must be greater than 0.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| if fee < 0: | ||||||||||||||||||||
| print(" Fee cannot be negative.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| nonce = chain.state.get_account(pk).get("nonce", 0) | ||||||||||||||||||||
| tx = Transaction(sender=pk, receiver=receiver, amount=amount, nonce=nonce) | ||||||||||||||||||||
| tx = Transaction(sender=pk, receiver=receiver, amount=amount, nonce=nonce, fee=fee) | ||||||||||||||||||||
| tx.sign(sk) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if mempool.add_transaction(tx): | ||||||||||||||||||||
|
|
@@ -231,6 +244,72 @@ async def cli_loop(sk, pk, chain, mempool, network): | |||||||||||||||||||
| else: | ||||||||||||||||||||
| print(" ❌ Transaction rejected (invalid sig, duplicate, or mempool full).") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # ── deploy ── | ||||||||||||||||||||
| elif cmd == "deploy": | ||||||||||||||||||||
| if len(parts) < 2: | ||||||||||||||||||||
| print(" Usage: deploy <filepath> [amount] [fee]") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| filepath = parts[1] | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| with open(filepath, "r") as f: | ||||||||||||||||||||
| code = f.read() | ||||||||||||||||||||
| except FileNotFoundError: | ||||||||||||||||||||
|
Comment on lines
+253
to
+256
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify async functions that still perform blocking open(...)
python - <<'PY'
import ast
from pathlib import Path
for p in Path(".").rglob("*.py"):
try:
tree = ast.parse(p.read_text(encoding="utf-8"))
except Exception:
continue
for node in ast.walk(tree):
if isinstance(node, ast.AsyncFunctionDef):
for sub in ast.walk(node):
if isinstance(sub, ast.Call) and isinstance(sub.func, ast.Name) and sub.func.id == "open":
print(f"{p}:{sub.lineno} async function '{node.name}' uses open(...)")
PYRepository: StabilityNexus/MiniChain Length of output: 121 Avoid blocking disk I/O on the
Suggested patch+from pathlib import Path
@@
- try:
- with open(filepath, "r") as f:
- code = f.read()
+ try:
+ code = await asyncio.to_thread(Path(filepath).read_text, encoding="utf-8")
except FileNotFoundError:
print(f" File not found: {filepath}")
continue📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.15.15)[warning] 254-254: Async functions should not open files with blocking methods like (ASYNC230) [warning] 254-254: Unnecessary mode argument Remove mode argument (UP015) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||||||
| print(f" File not found: {filepath}") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| try: | ||||||||||||||||||||
| amount = int(parts[2]) if len(parts) > 2 else 0 | ||||||||||||||||||||
| fee = int(parts[3]) if len(parts) > 3 else 0 | ||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||
| print(" Amount and fee must be integers.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if amount < 0 or fee < 0: | ||||||||||||||||||||
| print(" Amount and fee cannot be negative.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| nonce = chain.state.get_account(pk).get("nonce", 0) | ||||||||||||||||||||
| tx = Transaction(sender=pk, receiver=None, amount=amount, nonce=nonce, fee=fee, data=code) | ||||||||||||||||||||
| tx.sign(sk) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if mempool.add_transaction(tx): | ||||||||||||||||||||
| await network.broadcast_transaction(tx) | ||||||||||||||||||||
| print(f" ✅ Deploy Tx sent (nonce={nonce}). Mine a block to confirm.") | ||||||||||||||||||||
| else: | ||||||||||||||||||||
| print(" ❌ Deploy Transaction rejected.") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # ── call ── | ||||||||||||||||||||
| elif cmd == "call": | ||||||||||||||||||||
| if len(parts) < 3: | ||||||||||||||||||||
| print(" Usage: call <contract_address> <payload> [amount] [fee]") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| receiver = parts[1] | ||||||||||||||||||||
| if not is_valid_receiver(receiver): | ||||||||||||||||||||
| print(" Invalid receiver format. Expected 40 or 64 hex characters.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
| payload = parts[2] | ||||||||||||||||||||
|
|
||||||||||||||||||||
| try: | ||||||||||||||||||||
| amount = int(parts[3]) if len(parts) > 3 else 0 | ||||||||||||||||||||
| fee = int(parts[4]) if len(parts) > 4 else 0 | ||||||||||||||||||||
| except ValueError: | ||||||||||||||||||||
| print(" Amount and fee must be integers.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if amount < 0 or fee < 0: | ||||||||||||||||||||
| print(" Amount and fee cannot be negative.") | ||||||||||||||||||||
| continue | ||||||||||||||||||||
|
|
||||||||||||||||||||
| nonce = chain.state.get_account(pk).get("nonce", 0) | ||||||||||||||||||||
| tx = Transaction(sender=pk, receiver=receiver, amount=amount, nonce=nonce, fee=fee, data=payload) | ||||||||||||||||||||
| tx.sign(sk) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if mempool.add_transaction(tx): | ||||||||||||||||||||
| await network.broadcast_transaction(tx) | ||||||||||||||||||||
| print(f" ✅ Call Tx sent to {receiver[:12]}... (payload='{payload}'). Mine a block to confirm.") | ||||||||||||||||||||
| else: | ||||||||||||||||||||
| print(" ❌ Call Transaction rejected.") | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # ── mine ── | ||||||||||||||||||||
| elif cmd == "mine": | ||||||||||||||||||||
| mined = mine_and_process_block(chain, mempool, pk) | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align README CLI args with implemented command names.
Lines 121–123 document
[gas_limit], but the CLI parser uses[fee](seemain.pycommand usage strings). Please keep naming consistent to avoid failed command attempts.Suggested patch
📝 Committable suggestion
🤖 Prompt for AI Agents