def send_transaction( self, safe_address: str, safe_tx: SafeTx ) -> RelaySentTransaction: url = urljoin(self.base_url, f"/api/v1/safes/{safe_address}/transactions/") signatures = [] for i in range(len(safe_tx.signatures) // 65): v, r, s = signature_split(safe_tx.signatures, i) signatures.append( { "v": v, "r": r, "s": s, } ) data = { "to": safe_tx.to, "value": safe_tx.value, "data": safe_tx.data.hex() if safe_tx.data else None, "operation": safe_tx.operation, "gasToken": safe_tx.gas_token, "safeTxGas": safe_tx.safe_tx_gas, "dataGas": safe_tx.base_gas, "gasPrice": safe_tx.gas_price, "refundReceiver": safe_tx.refund_receiver, "nonce": safe_tx.safe_nonce, "signatures": signatures, } response = requests.post(url, json=data) if not response.ok: raise BaseAPIException(f"Error posting transaction: {response.content}") else: return RelaySentTransaction(response.json())
def send_transaction(self, safe_address: str, safe_tx: SafeTx) -> RelaySentTransaction: url = urljoin(self.base_url, f'/api/v1/safes/{safe_address}/transactions/') signatures = [] for i in range(len(safe_tx.signatures) // 65): v, r, s = signature_split(safe_tx.signatures, i) signatures.append( { "v": v, "r": r, "s": s, } ) data = { 'to': safe_tx.to, 'value': safe_tx.value, 'data': safe_tx.data.hex() if safe_tx.data else None, 'operation': safe_tx.operation, 'gasToken': safe_tx.gas_token, 'safeTxGas': safe_tx.safe_tx_gas, 'dataGas': safe_tx.base_gas, 'gasPrice': safe_tx.gas_price, 'refundReceiver': safe_tx.refund_receiver, 'nonce': safe_tx.safe_nonce, 'signatures': signatures, } response = requests.post(url, json=data) if not response.ok: raise BaseAPI(f'Error posting transaction: {response.content}') else: return RelaySentTransaction(response.json())
def parse_signature( cls, signatures: EthereumBytes, safe_tx_hash: EthereumBytes, ignore_trailing: bool = True, ) -> List["SafeSignature"]: """ :param signatures: One or more signatures appended. EIP1271 data at the end is supported. :param safe_tx_hash: :param ignore_trailing: Ignore trailing data on the signature. Some libraries pad it and add some zeroes at the end :return: List of SafeSignatures decoded """ if not signatures: return [] elif isinstance(signatures, str): signatures = HexBytes(signatures) signature_size = 65 # For contract signatures there'll be some data at the end data_position = len( signatures ) # For contract signatures, to stop parsing at data position safe_signatures = [] for i in range(0, len(signatures), signature_size): if ( i >= data_position ): # If contract signature data position is reached, stop break signature = signatures[i : i + signature_size] if ignore_trailing and len(signature) < 65: # Trailing stuff break v, r, s = signature_split(signature) signature_type = SafeSignatureType.from_v(v) safe_signature: "SafeSignature" if signature_type == SafeSignatureType.CONTRACT_SIGNATURE: if s < data_position: data_position = s contract_signature_len = int.from_bytes( signatures[s : s + 32], "big" ) # Len size is 32 bytes contract_signature = signatures[ s + 32 : s + 32 + contract_signature_len ] # Skip array size (32 bytes) safe_signature = SafeSignatureContract( signature, safe_tx_hash, contract_signature ) elif signature_type == SafeSignatureType.APPROVED_HASH: safe_signature = SafeSignatureApprovedHash(signature, safe_tx_hash) elif signature_type == SafeSignatureType.EOA: safe_signature = SafeSignatureEOA(signature, safe_tx_hash) elif signature_type == SafeSignatureType.ETH_SIGN: safe_signature = SafeSignatureEthSign(signature, safe_tx_hash) safe_signatures.append(safe_signature) return safe_signatures
def __init__(self, signature: EthereumBytes, safe_tx_hash: EthereumBytes, ethereum_client: EthereumClient): assert len(signature) > 65, 'Signature must be at least 65' # v = signature type, r = contract, s = offset of dynamic signature data self.v, self.r, self.s = signature_split(signature) assert self.v == 0, 'v must be 0' self.signature = HexBytes(signature) self.safe_tx_hash = safe_tx_hash self.ethereum_client = ethereum_client self.owner = checksum_encode(self.r) self.ok = self._check_signature()
def parse_signature(cls, signatures: EthereumBytes, safe_tx_hash: EthereumBytes) -> List['SafeSignature']: """ :param signatures: One or more signatures appended. EIP1271 data at the end is supported. :param safe_tx_hash: :return: List of SafeSignatures decoded """ if not signatures: return [] elif isinstance(signatures, str): signatures = HexBytes(signatures) signature_size = 65 # For contract signatures there'll be some data at the end data_position = len( signatures ) # For contract signatures, to stop parsing at data position safe_signatures = [] for i in range(0, len(signatures), signature_size): if i >= data_position: # If contract signature data position is reached, stop break signature = signatures[i:i + signature_size] v, r, s = signature_split(signature) signature_type = SafeSignatureType.from_v(v) safe_signature: 'SafeSignature' if signature_type == SafeSignatureType.CONTRACT_SIGNATURE: if s < data_position: data_position = s contract_signature_len = int.from_bytes( signatures[s:s + 32], 'big') # Len size is 32 bytes contract_signature = signatures[ s + 32:s + 32 + contract_signature_len] # Skip array size (32 bytes) safe_signature = SafeSignatureContract(signature, safe_tx_hash, contract_signature) elif signature_type == SafeSignatureType.APPROVED_HASH: safe_signature = SafeSignatureApprovedHash( signature, safe_tx_hash) elif signature_type == SafeSignatureType.EOA: safe_signature = SafeSignatureEOA(signature, safe_tx_hash) elif signature_type == SafeSignatureType.ETH_SIGN: safe_signature = SafeSignatureEthSign(signature, safe_tx_hash) safe_signatures.append(safe_signature) return safe_signatures
def __init__(self, signature: EthereumBytes, safe_tx_hash: EthereumBytes): self.signature = HexBytes(signature) self.safe_tx_hash = HexBytes(safe_tx_hash) self.v, self.r, self.s = signature_split(self.signature)
def __init__(self, signature: EthereumBytes, safe_tx_hash: EthereumBytes): self.signature = HexBytes(signature) self.v, self.r, self.s = signature_split(self.signature) self.signature_type = SafeSignatureType.from_v(self.v) self.owner = self.decode_owner(self.v, self.r, self.s, safe_tx_hash)