Skip to main content

Python SDK for interacting with SafeTea Multi-Signature Wallets

Project description

safteawallet-py

SafteaWallet-Py is a Python SDK for interacting with SafeTea Multi-Signature Wallets on Ethereum-compatible networks.

It provides a simple and modular interface to create new multi-signature wallets, manage transaction proposals, and execute approved transactions. Both synchronous and asynchronous interfaces are available.

Installation

Install the package via pip:

pip install safteawallet-py

Usage

Sync vs Async

Sync Async
Import safteawallet_py safteawallet_py.aio
Client SafeTeaClient AsyncSafeTeaClient
Usage Regular calls await all methods

Synchronous API

Connecting and Initializing the Client

from safteawallet_py.client import SafeTeaClient

client = SafeTeaClient(
    rpc_url="https://your.rpc.endpoint",
    factory_address="0xYourFactoryAddress",
    private_key="0x...",
)

Creating a New Multi-Signature Wallet

A minimum of two owners is required.

owners = [
    "0xOwnerAddress1",
    "0xOwnerAddress2",
    "0xOwnerAddress3",
]

result = client.factory.create_wallet(owners)
print(f"Wallet: {result.wallet_address}")
print(f"Tx hash: {result.transaction_hash}")

Proposing a Transaction

wallet = client.wallet("0xYourWalletAddress")

expiry = w3.eth.get_block("latest")["timestamp"] + 86400  # 24 hours

tx_index = wallet.submit_transaction(
    to="0xRecipientAddress",
    value=w3.to_wei(1, "ether"),
    data=b"",
    expiry=expiry,
)
print(f"Submitted at index: {tx_index}")

Confirming / Rejecting a Transaction

wallet.confirm_transaction(tx_index)
wallet.reject_transaction(tx_index)

Managing Owners

expiry = w3.eth.get_block("latest")["timestamp"] + 86400

wallet.propose_add_owner("0xNewOwnerAddress", expiry)
wallet.propose_remove_owner("0xOwnerToRemove", expiry)

wallet.confirm_owner_proposal(proposal_index)
wallet.reject_owner_proposal(proposal_index)

Async API (safteawallet_py.aio)

The async module mirrors the sync API but every method is a coroutine. Use it with asyncio or any async framework (FastAPI, aiohttp, etc.).

Client

from safteawallet_py.aio import AsyncSafeTeaClient

client = AsyncSafeTeaClient(
    rpc_url="https://your.rpc.endpoint",
    factory_address="0xYourFactoryAddress",
    private_key="0x...",          # optional — omit for read-only use
)

AsyncSafeTeaClient exposes two attributes after construction:

  • client.factory — an AsyncSafeTeaFactory instance
  • client.account — the LocalAccount derived from private_key, or None

Check connection

await client.check_connection()   # raises ConnectionError if unreachable

Get a wallet instance

wallet = client.wallet("0xYourWalletAddress")  # returns AsyncSafeTeaWallet

Factory — AsyncSafeTeaFactory

Create a wallet

from safteawallet_py.aio import AsyncSafeTeaClient

client = AsyncSafeTeaClient(rpc_url=..., factory_address=..., private_key=...)

owners = ["0xOwner1", "0xOwner2", "0xOwner3"]  # minimum 2
result = await client.factory.create_wallet(owners)

print(result.wallet_address)    # checksummed address of the new wallet
print(result.transaction_hash)  # hex tx hash

Raises ValueError if fewer than 2 owners are provided.

Get wallets for a user

wallets = await client.factory.get_user_wallets("0xUserAddress")
# returns List[ChecksumAddress]

Wallet — AsyncSafeTeaWallet

Obtain a wallet instance via client.wallet(address) or construct directly:

from safteawallet_py.aio.wallet import AsyncSafeTeaWallet

wallet = AsyncSafeTeaWallet(web3=client.web3, wallet_address="0x...", account=client.account)

All write methods require the account to be one of the wallet's owners, otherwise NotOwnerError is raised.


Info

# Full wallet snapshot
info = await wallet.get_info()
# info.address    — wallet address
# info.owners     — list of owner addresses
# info.threshold  — majority threshold (votes needed to execute)

# Individual getters
owners    = await wallet.get_owners()     # List[ChecksumAddress]
threshold = await wallet.get_threshold()  # int

Transactions

# Submit a new proposal
expiry = int(time.time()) + 3600   # unix timestamp, must be within 30 days

tx_index = await wallet.submit_transaction(
    to="0xRecipientAddress",
    value=1_000_000_000_000_000_000,  # wei
    data=b"",                          # optional calldata
    expiry=expiry,                     # optional, default 3600s from now
)

# Confirm or reject
await wallet.confirm_transaction(tx_index)
await wallet.reject_transaction(tx_index)

# Read
count = await wallet.get_transaction_count()   # int
tx    = await wallet.get_transaction(tx_index) # Transaction dataclass

Transaction fields:

Field Type Description
to str Recipient address
value int Value in wei
data bytes Calldata
status TxStatus PENDING, EXECUTED, or CANCELED
confirmations int Confirmation vote count
rejections int Rejection vote count
expiry int Unix timestamp
created_at int Unix timestamp

confirm_transaction and reject_transaction validate state before sending and raise:

  • SafeTeaError — if already executed or canceled
  • AlreadyVotedError — if the caller already voted
  • TransactionExpiredError — if the proposal has expired

Owner proposals

expiry = int(time.time()) + 3600

# Propose changes
await wallet.propose_add_owner("0xNewOwner", expiry)
await wallet.propose_remove_owner("0xOwnerToRemove", expiry)

# Vote on a proposal
await wallet.confirm_owner_proposal(proposal_index)
await wallet.reject_owner_proposal(proposal_index)

# Read
count    = await wallet.get_owner_proposal_count()      # int
proposal = await wallet.get_owner_proposal(index)       # OwnerProposal dataclass

OwnerProposal fields:

Field Type Description
proposed_owner str Address being added or removed
proposal_type ProposalType ADD or REMOVE
status TxStatus PENDING, EXECUTED, or CANCELED
confirmations int Confirmation vote count
rejections int Rejection vote count
expiry int Unix timestamp
created_at int Unix timestamp

confirm_owner_proposal and reject_owner_proposal raise ProposalNotFoundError if the index is out of range.


Full async example

import asyncio
from safteawallet_py.aio import AsyncSafeTeaClient

async def main():
    client = AsyncSafeTeaClient(
        rpc_url="https://your.rpc.endpoint",
        factory_address="0xFactoryAddress",
        private_key="0xYourPrivateKey",
    )

    await client.check_connection()

    # Deploy a new wallet
    result = await client.factory.create_wallet([
        "0xOwner1",
        "0xOwner2",
    ])
    print("Wallet deployed:", result.wallet_address)

    wallet = client.wallet(result.wallet_address)

    # Submit a transaction
    import time
    tx_index = await wallet.submit_transaction(
        to="0xRecipient",
        value=10 ** 18,   # 1 ETH in wei
        expiry=int(time.time()) + 3600,
    )
    print("Submitted tx index:", tx_index)

    # Second owner confirms
    client2 = AsyncSafeTeaClient(
        rpc_url="https://your.rpc.endpoint",
        factory_address="0xFactoryAddress",
        private_key="0xOwner2PrivateKey",
    )
    wallet2 = client2.wallet(result.wallet_address)
    await wallet2.confirm_transaction(tx_index)
    print("Transaction confirmed and executed.")

asyncio.run(main())

Exceptions

Exception Description
SafeTeaError Base exception for all SDK errors
NotOwnerError Caller is not a wallet owner
AlreadyVotedError Caller already voted on this proposal
TransactionExpiredError Proposal has passed its expiry timestamp
ProposalNotFoundError Owner proposal index does not exist
ConfigurationError Client misconfigured (e.g. write op without a signer)

Development

To run the test suite:

pytest tests/

License

This project is open-source and available under the MIT License.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

safteawallet_py-0.2.0.tar.gz (21.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

safteawallet_py-0.2.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file safteawallet_py-0.2.0.tar.gz.

File metadata

  • Download URL: safteawallet_py-0.2.0.tar.gz
  • Upload date:
  • Size: 21.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for safteawallet_py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 76b71ef773e50c0b992e5195f9f5f16093489455c2a2e7d7c55d2a51d8c61d4d
MD5 5acd0eb1cd751f1921ec1a5943eaecc4
BLAKE2b-256 1bf807cd12ec5912d93d70477fcd0872c1f635726dc3599df5ef5ae7bbdda7bf

See more details on using hashes here.

File details

Details for the file safteawallet_py-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for safteawallet_py-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d129595086bc59e5a0f4ea7049c05ea13a298fd49662302775b26efdb34ccf94
MD5 1f397d60a2779f0413109932dababe1d
BLAKE2b-256 990665253277ffb51a21c9e970d0d515232b25469734cef1a792fe3b9e85b3c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page