mirror of
https://github.com/status-im/status-python-sdk.git
synced 2026-08-30 21:51:14 +00:00
wallet: Send ERC-20 and ETH on the same chain
This commit is contained in:
+63
-1
@@ -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
@@ -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`
|
||||
|
||||
@@ -3,3 +3,4 @@ websocket-client
|
||||
websockets
|
||||
pandas
|
||||
pillow
|
||||
eth-abi
|
||||
|
||||
Reference in New Issue
Block a user