wallet: Send ERC-20 and ETH on the same chain

This commit is contained in:
Nick Ninov
2026-06-17 19:49:56 +03:00
parent 194c39eeae
commit 20c008f7fa
3 changed files with 185 additions and 1 deletions
+63 -1
View File
@@ -1,5 +1,5 @@
from typing import Optional, Union, Generator, Any
import requests, datetime, re, logging, os, json, ast, shutil
import requests, datetime, re, logging, os, json, ast, shutil, eth_abi
import pandas as pd
from PIL import Image
from PIL.JpegImagePlugin import JpegImageFile
@@ -25,6 +25,10 @@ class Account:
"account": "accounts",
"identity": "multiaccounts"
}
__keccak256_selectors = {
"transfer": "a9059cbb" # keccak256("transfer(address,uint256)")[:4]
}
def __init__(self, domain: str = "localhost", port: int = 8080, is_secure: bool = False):
"""
Work with your own Status App account
@@ -77,6 +81,7 @@ class Account:
"create_backup": f"{self.__http_base_url}PerformLocalBackup",
"load_backup": f"{self.__http_base_url}LoadLocalBackup",
"rpc": f"{self.__http_base_url}CallRPC",
"transaction": f"{self.__http_base_url}SendTransactionV2"
},
"socket": {
"signals": f"{self.__ws_base_url}signals"
@@ -995,6 +1000,63 @@ class Account:
market_info = market_info.rename(columns=column_mapping)[list(column_mapping.values())]
return market_info.copy()
def send_transaction(self, address: str, symbol: str, amount: float, chain_id: int = 1) -> Optional[str]:
"""
Send crypto to specified `address`
Parameters:
- `address` - the wallet address of the receiver
- `symbol` - either a valid Status token symbol from `def get_tokens()` or its address
- `amount` - the amount that will be sent to the `address`
- `chain_id` - valid Chain from `self.chains`
Output:
- Transaction hash that to monitor the transactions progress
"""
is_eth = symbol == "ETH"
is_address = symbol.startswith("0x")
symbol = symbol.upper()
tokens = self.get_tokens()[["chain_id", "address", "symbol", "decimals"]].drop_duplicates().reset_index(drop=True)
query = (tokens["address" if is_address else "symbol"] == symbol) & (tokens["chain_id"] == chain_id)
if query.sum() == 0:
raise Exception(f"Given {'address' if is_address else 'symbol'} {symbol} on chain ID {chain_id} does not exist...")
token_info = tokens.loc[query].to_dict("records")[0]
balance = self.balance
query = (balance["address"] == token_info["address"]) & (balance["chain_id"] == chain_id)
if query.sum() == 0:
raise Exception(f"Given {'address' if is_address else 'symbol'} {symbol} on chain ID {chain_id} was not found in your wallet ({self.info['wallet_address']})...")
wallet_amount = balance.loc[query].reset_index(drop=True)["amount"].iloc[0]
if amount > wallet_amount:
raise Exception(f"Given {'address' if is_address else 'symbol'} {symbol} on chain ID {chain_id} has {wallet_amount} but you are trying to send {amount}...")
raw_amount = int(amount * (10**token_info["decimals"]))
tx = {
"version": 1,
"from": self.info["wallet_address"],
"to": address if is_eth else token_info["address"],
"value": hex(raw_amount) if is_eth else "0x0",
"fromChainID": chain_id,
"toChainID": chain_id,
}
if not is_eth:
encoded_args = eth_abi.encode(["address", "uint256"], [address, raw_amount]).hex()
tx["data"] = "0x" + self.__keccak256_selectors["transfer"] + encoded_args
payload = {
"password": self.info["password"],
"txArgs": tx
}
response = requests.post(self.__urls["http"]["transaction"], json=payload)
transaction_hash: str = response.json().get("result")
url = f"http://etherscan.io/tx/{transaction_hash}"
self.logger.info(f"Transaction: {url}")
return transaction_hash
def __start_messenger(self):
"""
Start the decentralized messaging service.
+121
View File
@@ -583,6 +583,127 @@ Returns `pd.DataFrame`.
| `pct_change_24hr` | `float` | Percentage price change over the last 24 hours. |
#### `send_transaction(address, symbol, amount, chain_id=1)`
Send crypto from the logged-in account's wallet to another wallet address on the same chain. This method supports both **ETH** and **ERC-20** tokens. The token can be identified either by its Status symbol (e.g. `ETH`, `SNT`, `USDT`) or by its contract address. Before broadcasting, the method validates that the token exists on the given chain and that the wallet holds enough balance for the requested `amount`.
| Name | Type | Required | Description |
|-----|-----|-----|-------------|
| `address` | `str` | Yes | The wallet address of the receiver. |
| `symbol` | `str` | Yes | Either a valid Status token symbol from [`get_tokens`](./account.md#get_tokens) or the token's contract address (must start with `0x`). |
| `amount` | `float` | Yes | The amount of the token to send. Must be less than or equal to the wallet's current balance for that token. |
| `chain_id` | `int` | No | Chain ID where the transaction will be broadcast. Defaults to `1` (Ethereum mainnet). All available chain IDs can be obtained from the [`chains`](./account.md#chains) property. |
Returns `str` representing the **transaction hash**. The hash can be appended to `https://etherscan.io/tx/` to monitor the transaction's progress. The transaction URL is also written to [`logger`](./account.md#logger) at `INFO` level. If the backend fails to broadcast and does not return a hash, `None` is returned instead.
Send ETH:
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP",
"infura_token" : "token from https://www.infura.io/",
"coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
tx_hash = account.send_transaction(
address=vitalik_address,
symbol="ETH",
amount=0.01
)
print(f"Transaction: https://etherscan.io/tx/{tx_hash}")
```
Send an ERC-20 token by symbol:
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP",
"infura_token" : "token from https://www.infura.io/",
"coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
tx_hash = account.send_transaction(
address=vitalik_address,
symbol="SNT",
amount=10
)
```
Send an ERC-20 token by contract address:
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP",
"infura_token" : "token from https://www.infura.io/",
"coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
snt_address = "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
tx_hash = account.send_transaction(
address=vitalik_address,
symbol=snt_address,
amount=10
)
```
Send on a different chain:
```python
from bot import Account
account = Account()
params = {
"display_name": "status-app-bot",
"password": "SNTPUMP",
"infura_token" : "token from https://www.infura.io/",
"coingecko_api_key": "API key from https://www.coingecko.com/"
}
account.login(**params)
vitalik_address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
tx_hash = account.send_transaction(
address=vitalik_address,
symbol="ETH",
amount=0.01,
chain_id=10 # Optimism
)
```
**Note**: This is a wallet method, so it requires both `infura_token` and `coingecko_api_key` to be provided in [`login`](./account.md#loginpassword-key_uidnone-display_namenone-mnemonicnone-infura_tokennonecoingecko_api_keynone). If either is missing, an exception will be raised when this method is called.
**Note**: The sender and receiver must be on the **same chain**. Cross-chain transfers are not supported by this method — set `chain_id` to the chain where the funds currently exist.
**Note**: An **exception will be raised** when:
- the `symbol` (or contract address) does not exist on the given `chain_id`
- the token is not present in the logged-in wallet's balance
- the requested `amount` exceeds the current wallet balance
## Properties
### `available_accounts`
+1
View File
@@ -3,3 +3,4 @@ websocket-client
websockets
pandas
pillow
eth-abi