diff --git a/scripts/deploy_contract.sh b/scripts/deploy_contract.sh new file mode 100755 index 000000000..f83598fa2 --- /dev/null +++ b/scripts/deploy_contract.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Example usage: ./deploy_contract.sh --contract-hex-path ./misc/registry.hex --private-key-path ./misc/private_key.txt --http-provider-url http://localhost:8545 + +# Function to display usage +usage() { + echo "Usage: $0 --contract-hex-path --private-key-path [--http-provider-url ]" + exit 1 +} + +# Default HTTP provider URL +HTTP_PROVIDER="http://localhost:8545" + +# Parse named arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --contract-hex-path) CONTRACT_HEX_PATH="$2"; shift ;; + --private-key-path) PRIVATE_KEY_PATH="$2"; shift ;; + --http-provider-url) HTTP_PROVIDER="$2"; shift ;; + *) usage ;; + esac + shift +done + +if [ -z "$CONTRACT_HEX_PATH" ]; then + usage +fi + +# Read the private key from the file +PRIVATE_KEY=$(cat "$PRIVATE_KEY_PATH") + +# Get the block number at the start of the script +BLOCK_NUMBER=$(cast block-number --rpc-url $HTTP_PROVIDER) +echo "Block number at start of script: $BLOCK_NUMBER" + +# Get the sender address using the private key +SENDER_ADDRESS=$(cast wallet address --private-key $PRIVATE_KEY) +echo "Sender address: $SENDER_ADDRESS" + +# Get the balance of the sender address +BALANCE=$(cast balance $SENDER_ADDRESS --rpc-url $HTTP_PROVIDER) +echo "Balance at start of script: $BALANCE" + +# Read or compile the contract code +if [ -n "$CONTRACT_HEX_PATH" ]; then + # Read the contract code from the hex file + CONTRACT_CODE=$(cat "$CONTRACT_HEX_PATH" | tr -d '\n') +fi + +# Prepend 0x to the contract code +CONTRACT_CODE="0x$CONTRACT_CODE" + +# Deploy the contract +RECEIPT=$(cast send --private-key $PRIVATE_KEY --rpc-url $HTTP_PROVIDER --create $CONTRACT_CODE) +TX_HASH=$(echo "$RECEIPT" | grep 'transactionHash' | awk '{print $2}') +CONTRACT_ADDRESS=$(echo "$RECEIPT" | grep 'contractAddress' | awk '{print $2}') +echo "Transaction hash: $TX_HASH" + +# Wait for the transaction to be mined +while true; do + RECEIPT=$(cast tx $TX_HASH --rpc-url $HTTP_PROVIDER) + if [ "$RECEIPT" != "null" ]; then + break + fi + sleep 1 +done + +# Get the block number at the end of the script +BLOCK_NUMBER=$(cast block-number --rpc-url $HTTP_PROVIDER) +echo "Block number at end of script: $BLOCK_NUMBER" + +# Get the balance of the sender address +BALANCE=$(cast balance $SENDER_ADDRESS --rpc-url $HTTP_PROVIDER) +echo "Balance at end of script: $BALANCE" + +# Echo the contract address +echo "Contract address: $CONTRACT_ADDRESS" diff --git a/scripts/ulm_client.py b/scripts/ulm_client.py new file mode 100644 index 000000000..4ec91804f --- /dev/null +++ b/scripts/ulm_client.py @@ -0,0 +1,227 @@ +import os +import unittest + +from eth_abi import decode, encode +from web3 import Web3 +from web3.middleware import SignAndSendRawMiddlewareBuilder + +class ulm_client: + """Python interface to ULM""" + + def setUp(self): + """Set up test environment with web3 connection and account details""" + # Initialize web3 connection + self.rpc_url = os.getenv("RPC_URL") + self.web3 = Web3(Web3.HTTPProvider(self.rpc_url)) + + # Set up test account + self.test_account_private_key = os.getenv("TEST_ACCOUNT_PRIVATE_KEY") + self.test_account = self.web3.eth.account.from_key( + self.test_account_private_key + ) + self.test_account_address = self.test_account.address + + # Get contract addresses from environment + self.registry_address = os.getenv("REGISTRY_ADDRESS") + self.wbtc_address = os.getenv("WBTC_ADDRESS") + self.pi2_address = os.getenv("PI2_ADDRESS") + self.usdc_address = os.getenv("USDC_ADDRESS") + self.weth_address = os.getenv("WETH_ADDRESS") + + # Add middleware to automatically sign transactions + self.web3.middleware_onion.inject( + SignAndSendRawMiddlewareBuilder.build(self.test_account), layer=0 + ) + + def function_selector(self, function_signature): + """Calculate function selector from signature + + Args: + function_signature: String of function name and parameters + + Returns: + bytes: First 4 bytes of keccak hash of function signature + """ + return self.web3.keccak(text=function_signature)[:4] + + def mint_token(self, token_address): + # Mint + mint_selector = self.function_selector("mint(address,uint256)") + mint_data = encode( + ["address", "uint256"], + [self.test_account_address, 1000000000000000000000000], + ) # 1,000,000 tokens + mint_tx = self.web3.eth.send_transaction( + { + "from": self.test_account_address, + "to": token_address, + "data": mint_selector + mint_data, + } + ) + mint_tx_receipt = self.web3.eth.wait_for_transaction_receipt(mint_tx) + self.assertEqual(mint_tx_receipt["status"], 1) + + # Verify balance + get_token_balance_selector = self.function_selector("balanceOf(address)") + get_token_balance_data = encode(["address"], [self.test_account_address]) + get_token_balance_result = self.web3.eth.call( + { + "to": token_address, + "data": get_token_balance_selector + get_token_balance_data, + } + ) + token_a_balance = int.from_bytes(get_token_balance_result, "big") + + self.assertEqual(token_a_balance, 1000000000000000000000000) + + def approve_token(self, token_address): + """Test approving spending of tokens""" + # Approve token A + approve_selector = self.function_selector("approve(address,uint256)") + approve_data = encode( + ["address", "uint256"], [token_address, 500000000000000000000000] + ) # 500,000 tokens + approve_token_tx = self.web3.eth.send_transaction( + { + "from": self.test_account_address, + "to": token_address, + "data": approve_selector + approve_data, + } + ) + approve_token_tx_receipt = self.web3.eth.wait_for_transaction_receipt( + approve_token_tx + ) + self.assertEqual(approve_token_tx_receipt["status"], 1) + + # Verify token A allowance + allowance_selector = self.function_selector("allowance(address,address)") + allowance_token_data = encode( + ["address", "address"], [self.test_account_address, token_address] + ) + allowance_token_result = self.web3.eth.call( + { + "to": token_address, + "data": allowance_selector + allowance_token_data, + } + ) + token_allowance = int.from_bytes(allowance_token_result, "big") + self.assertEqual(token_allowance, 500000000000000000000000) + + def check_token_identity( + self, token_address, expected_name, expected_decimals, expected_symbol + ): + """Read token identity""" + token_identity_selector = self.function_selector("name()") + token_identity_data = encode([], []) + token_identity_result = self.web3.eth.call( + { + "to": token_address, + "data": token_identity_selector + token_identity_data, + } + ) + + name = decode(["string"], token_identity_result)[0] + self.assertEqual(name, expected_name) + + token_identity_selector = self.function_selector("decimals()") + token_identity_data = encode([], []) + token_identity_result = self.web3.eth.call( + { + "to": token_address, + "data": token_identity_selector + token_identity_data, + } + ) + self.assertEqual( + int.from_bytes(token_identity_result, "big"), expected_decimals + ) + + token_identity_selector = self.function_selector("symbol()") + token_identity_data = encode([], []) + token_identity_result = self.web3.eth.call( + { + "to": token_address, + "data": token_identity_selector + token_identity_data, + } + ) + + symbol = decode(["string"], token_identity_result)[0] + self.assertEqual(symbol, expected_symbol) + + def test_02_mint_wbtc(self): + """Test minting WBTC (Rust) to test account""" + self.mint_token(self.wbtc_address) + + def test_03_mint_pi2(self): + """Test minting PI2 (Simple) to test account""" + self.mint_token(self.pi2_address) + + def test_04_mint_usdc(self): + """Test minting USDC (Solidity) to test account""" + self.mint_token(self.usdc_address) + + def test_05_mint_weth(self): + """Test minting WETH (EVM) to test account""" + # Mint + mint_selector = self.function_selector("deposit()") + mint_data = encode([], []) + mint_tx = self.web3.eth.send_transaction( + { + "from": self.test_account_address, + "to": self.weth_address, + "data": mint_selector + mint_data, + "value": 10000000000000000, + } + ) + mint_tx_receipt = self.web3.eth.wait_for_transaction_receipt(mint_tx) + self.assertEqual(mint_tx_receipt["status"], 1) + + # Verify balance + get_token_balance_selector = self.function_selector("balanceOf(address)") + get_token_balance_data = encode(["address"], [self.test_account_address]) + get_token_balance_result = self.web3.eth.call( + { + "to": self.weth_address, + "data": get_token_balance_selector + get_token_balance_data, + } + ) + token_a_balance = int.from_bytes(get_token_balance_result, "big") + + self.assertEqual(token_a_balance, 10000000000000000) + + get_eth_balance_result = self.web3.eth.get_balance(self.weth_address) + self.assertEqual(get_eth_balance_result, 10000000000000000) + + def test_06_approve_wbtc(self): + """Test approving spending of WBTC (Rust)""" + self.approve_token(self.wbtc_address) + + def test_07_approve_pi2(self): + """Test approving spending of PI2 (Simple)""" + self.approve_token(self.pi2_address) + + def test_08_approve_usdc(self): + """Test approving spending of USDC (Solidity)""" + self.approve_token(self.usdc_address) + + def test_09_approve_weth(self): + """Test approving spending of WETH (EVM)""" + self.approve_token(self.weth_address) + + def test_10_wbtc_identity(self): + """Test WBTC (Rust) identity""" + self.check_token_identity(self.wbtc_address, "Wrapped Bitcoin", 18, "WBTC") + + def test_11_pi2_identity(self): + """Test PI2 (Simple) identity""" + self.check_token_identity(self.pi2_address, "Pi Squared", 18, "PI2") + + def test_12_usdc_identity(self): + """Test USDC (Solidity) identity""" + self.check_token_identity(self.usdc_address, "USD Coin", 18, "USDC") + + def test_13_weth_identity(self): + """Test WETH (EVM) identity""" + self.check_token_identity(self.weth_address, "Wrapped Ethereum", 18, "WETH") + +if __name__ == "__main__": + pass