From 79955b5f2ea9d10d8113957718591ba71e45c83a Mon Sep 17 00:00:00 2001 From: gburry-fireblocks Date: Fri, 18 Nov 2022 11:53:38 -0800 Subject: [PATCH] feat: fix XLM withdrawal & support XDB - Update Stellar SDK package and fix XLM withdrawal - Add constants for DigitalBits (XDB) network passphrase, RPC URL, and explorer URL - Format `xlm_helpers.py` --- .gitignore | 2 + requirements.txt | 4 +- utils/xlm_helpers.py | 123 +++++++++++++++++++++++++++++++------------ 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index 894a44c..2802709 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,5 @@ venv.bak/ # mypy .mypy_cache/ + +.DS_Store \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5dc09de..257f841 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,9 @@ termcolor==1.1.0 substrate-interface==1.1.4 py-algorand-sdk==1.8.0 solana==0.19.1 -stellar_base==1.1.2.0 +stellar-sdk==8.1.1 +requests==2.28.1 +crc16==0.1.1 gql==3.4.0 cbor2==5.4.3 bech32==1.2.0 diff --git a/utils/xlm_helpers.py b/utils/xlm_helpers.py index 7582d4e..ad40524 100644 --- a/utils/xlm_helpers.py +++ b/utils/xlm_helpers.py @@ -1,25 +1,45 @@ import base64 import struct -from stellar_base.builder import Builder -from stellar_base.address import Address -from stellar_base.stellarxdr import Xdr -from stellar_base.keypair import Keypair +import requests from crc16 import crc16xmodem - +from stellar_sdk import stellar_xdr, Server, Network, Asset, Keypair, SignerKey, SignerKeyType, TransactionBuilder from utils import eddsa_sign -BIP_44_CONSTANT = 44 -XLM_ASSET_NUM = 146 -CHANGE = 0 -ADDR_INDEX = 0 + +""" +Set the variables `NETWORK_PASSPHRASE`, `RPC_URL`, and `EXPLORER_URL` for the appropriate chain. +""" + +# Network passphrase +XLM_LIVENET_PASSPHRASE = Network.PUBLIC_NETWORK_PASSPHRASE +XLM_TESTNET_PASSPHRASE = Network.TESTNET_NETWORK_PASSPHRASE +XDB_LIVENET_PASSPHRASE = 'LiveNet Global DigitalBits Network ; February 2021' +XDB_TESTNET_PASSPHRASE = 'TestNet Global DigitalBits Network ; December 2020' +NETWORK_PASSPHRASE = XLM_LIVENET_PASSPHRASE + +# RPC URL +XLM_LIVENET_RPC_URL = 'https://horizon.stellar.org' +XLM_TESTNET_RPC_URL = 'https://horizon-testnet.stellar.org' +XDB_LIVENET_RPC_URL = 'https://frontier.livenet.digitalbits.io' +XDB_TESTNET_RPC_URL = 'https://frontier.testnet.digitalbits.io' +RPC_URL = XLM_LIVENET_RPC_URL + +# Explorer URL +XLM_EXPLORER_URL = 'https://stellarchain.io/transactions' +XDB_EXPLORER_URL = 'https://xdbexplorer.com/transaction' +EXPLORER_URL = XLM_EXPLORER_URL + class RawKeypair(Keypair): def __init__(self, signing_key) -> None: self.signing_key = signing_key self.verify_key = eddsa_sign.private_key_to_public_key(signing_key) - - def xdr_public_key(self) -> Xdr.types.PublicKey: - return Xdr.types.PublicKey(Xdr.const.KEY_TYPE_ED25519, self.verify_key) + + def xdr_signing_key(self) -> SignerKey: + return SignerKey(self.signing_key, SignerKeyType.SIGNER_KEY_TYPE_ED25519) + + def xdr_public_key(self) -> stellar_xdr.PublicKey: + return stellar_xdr.PublicKey(SignerKeyType.SIGNER_KEY_TYPE_ED25519, self.verify_key) def raw_public_key(self) -> bytes: return self.verify_key @@ -30,49 +50,82 @@ def signature_hint(self) -> bytes: def sign(self, data: bytes) -> bytes: return eddsa_sign.eddsa_sign(self.signing_key, data) -def withdraw(key, to_address, amount = None, dst_tag = None): + +def withdraw(key, to_address, amount=None, dst_tag=None): + server = Server(horizon_url=RPC_URL) + keypair = RawKeypair(key) - builder = Builder(address=public_key_to_address(keypair.raw_public_key()), network='PUBLIC') + + builder = TransactionBuilder( + source_account=server.load_account(keypair), + network_passphrase=NETWORK_PASSPHRASE, + base_fee=100 + ) if amount is None: builder.append_account_merge_op(to_address) else: - builder.append_payment_op(to_address, str(round(float(amount),7)), 'XLM') + builder.append_payment_op( + to_address, Asset.native(), str(round(float(amount), 7))) + if dst_tag is not None: - builder.add_text_memo(dst_tag) - builder.keypair = keypair - builder.sign() - ret = builder.submit() - if ret['successful']: - return ret['hash'] - print(ret) - return None - -def getBalance(addr): - address = Address(address=addr, network='PUBLIC') - address.get() + builder.add_text_memo(dst_tag) + + tx = builder.build() + + tx.sign(keypair) + + res = server.submit_transaction(tx) + + if res['successful']: + print(f"{EXPLORER_URL}/{res['hash']}") + return res['hash'] + print(res) + + +def get_balance(addr): + url = f'{RPC_URL}/accounts/{addr}' + + res = requests.get(url) + + if res.status_code == 404: + raise Exception('Account not found') + + account = res.json() + balance = 0 - for b in address.balances: + + for b in account['balances']: if b['asset_type'] == 'native': balance = float(b['balance']) break + return balance + def public_key_to_address(public_key: bytes) -> str: if public_key is None: raise ValueError("cannot encode null public_key") version_byte = b'0' payload = version_byte + public_key - crc = struct.pack("