0

I have a smart contract deployed. Whenever I try to perform a transaction, the transaction starts, but that's it. The transaction never actually takes place (or "doesn't get mined" might be the right word).

For reference, my smart contract function has takes one parameter and gives no output. just stores it. It's not that complex.

I do have reason to believe this has something to do with the transaction manager that I have set and how it doesn't provide enough nonce for any miner to take into consideration, since that was one of the answers on another question. However, they never said what the method was to fix it. I tried my best to fix it using a static gasProvider in hopes that it might be enough.

I have two pieces of code below:

  1. VoteContractDelegate - This is used by rest of the application to make requests.
  2. VoteContractAccessor - This extends the contract class and is used to return remotecall requests.

Note: I am using the web3j library.

VoteContractDelegate

    private const val TAG = "VoteContractDelegate"

class VoteContractDelegate() {
    //-----------------------------------------------------------------------------contract-elements
    // TODO: get this from data store
    private val USER_PRIVATE_KEY =
        "66c53799ee0c63f2564305e738ea7479d7aee84aed3aac4c01e54a7acbcc4d92"
    private val ROPSTEN_INFURA_URL = "https://ropsten.infura.io/v3/c358089e1aaa4746aa50e61d4ec41c5c"

    private val credentials = Credentials.create(USER_PRIVATE_KEY)

    private lateinit var web3j: Web3j//--------------------------------------------works-as-intended
    private lateinit var contract: VoteContractAccessor

    // TODO: set correct value
    private val gasPrice: BigInteger = Convert.toWei("40", Convert.Unit.GWEI).toBigInteger()

    //this gas limit value is from deployment and has to be constant
    private val gasLimit = BigInteger.valueOf(4000000)

    private val gasProvider: ContractGasProvider = StaticGasProvider(gasPrice, gasLimit)

    init {
        instantiateWeb3J()
        web3j.ethGetTransactionCount(credentials.address, DefaultBlockParameterName.LATEST)
        initializeContract()
    }

    private fun instantiateWeb3J() {
        Log.d(
            TAG, try {
                web3j = Web3j.build(HttpService(ROPSTEN_INFURA_URL))
                "Connection Successful"
            } catch (e: Exception) {
                e.printStackTrace()
                "Connection Unsuccessful, error : ${e.message}"
            }
        )
    }

    private fun initializeContract() {
        Log.d(
            TAG, try {
                contract = VoteContractAccessor(
                    web3j,
                    RawTransactionManager(web3j, credentials, ChainIdLong.ROPSTEN),
                    newGasProvider, credentials
                )
                "Contract Initialized Successfully"
            } catch (e: Exception) {
                "Contract Initialization Error"
            }
        )
    }

    // TODO: this is the problem function...
    fun casteVote(party: PartyEnum): String {
        return try {
            Log.d(TAG,"started casteVote")
            /**
             * this starts but never completes. No errors outputted.
             * If you watch closely, You'll see a '9' inside the get.
             * that is the timeout duration. This function times out
             * before being executed. It keeps waiting for whole 9
             * minutes and then outputs a timeout error
             */
            "cost = " + (
                    contract
                        .putVote(party)
                        .sendAsync()
                        .get(9, TimeUnit.MINUTES)
                        .gasUsed
                        .toString()
                    )
            /**
             * returns null as a result.
             */
        } catch (e: Exception) {
            e.printStackTrace()
            e.message.toString()
        }
    }
}

VoteContractAccessor:

private const val TAG = "VoteContractAccessor"
private const val CONTRACT_BINARY =
    "608060405234801561001057600080fd5b506000600281905560038190556004556103be8061002f6000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c8063449b6db21161005b578063449b6db2146100b8578063464d4a92146100db578063a483f4e1146100ee578063f5a31579146100f657600080fd5b8063068ea0eb146100825780630efcb43c1461008c57806342cff738146100a3575b600080fd5b61008a6100fe565b005b6004545b6040519081526020015b60405180910390f35b6100ab6101cb565b60405161009a9190610312565b3360009081526020819052604090205460ff16604051901515815260200161009a565b61008a6100e93660046102f9565b61022d565b600354610090565b600254610090565b3360009081526020819052604090205460ff16156101635760405162461bcd60e51b815260206004820152601b60248201527f596f75204861766520416c7265616479204265656e204164646564000000000060448201526064015b60405180910390fd5b336000818152602081905260408120805460ff191660019081179091558054808201825591527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf601805473ffffffffffffffffffffffffffffffffffffffff19169091179055565b6060600180548060200260200160405190810160405280929190818152602001828054801561022357602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610205575b5050505050905090565b60048110801561023d5750600081115b6102af5760405162461bcd60e51b815260206004820152603960248201527f74686520676976656e206e756d62657220697320696e76616c6964206173207460448201527f6865206e756d626572206973206f7574206f662072616e676500000000000000606482015260840161015a565b80600114156102d057600280549060006102c88361035f565b919050555050565b80600214156102e957600380549060006102c88361035f565b600480549060006102c88361035f565b60006020828403121561030b57600080fd5b5035919050565b6020808252825182820181905260009190848201906040850190845b818110156103535783516001600160a01b03168352928401929184019160010161032e565b50909695505050505050565b600060001982141561038157634e487b7160e01b600052601160045260246000fd5b506001019056fea264697066735822122036c7a6eb905bfb9bbcbf2436975878a32327f0fd4a8648f67281fd07be0ff4b064736f6c63430008070033"
private const val CONTRACT_ADDRESS = "0x84D46ba7aAac6221DF9038d3Ccf41F1cd46001aF"

class VoteContractAccessor(
    private val web3obj: Web3j,
    transactionManager: TransactionManager,
    gasProvider: ContractGasProvider,
    private val credentials: Credentials
) :
    Contract(
        CONTRACT_BINARY,
        CONTRACT_ADDRESS,
        web3obj,
        transactionManager,
        gasProvider
    ) {
    //--------------------------------------------------------------------------------function-names
    private val functionGetHasAlreadyVoted = "hasAlreadyVoted"
    private val functionRegisterVote = "registerVote"
    private val functionGetAddressValues = "getAddressValues"
    private val functionGetPartyOneVotes = "getParty1Votes"
    private val functionGetPartyTwoVotes = "getParty2Votes"
    private val functionGetPartyThreeVotes = "getParty3Votes"

    fun printBalance(): BigDecimal {
        val balance =
            Convert.fromWei(
                web3obj.ethGetBalance(
                    credentials.address, DefaultBlockParameterName.LATEST
                )
                    .send().balance.toString(),
                Convert.Unit.ETHER
            )

        Log.d(TAG, "user balance = $balance")
        return balance
    }

    fun putVote(party: PartyEnum): RemoteCall<TransactionReceipt> {
        Log.d(TAG, "putVote\n\n\nstarted")
        val function = Function(
            functionRegisterVote,
            listOf<Type<*>>(
                Uint256(
                    when (party) {
                        PartyEnum.ONE -> 1
                        PartyEnum.TWO -> 2
                        PartyEnum.THREE -> 3
                    }
                )
            ),
            emptyList()
        )
        Log.d(TAG, "gas price = ${gasProvider.getGasPrice(functionRegisterVote)}")
        return executeRemoteCallTransaction(function)
    }
}

PartyEnum

enum class PartyEnum {
    ONE, TWO, THREE
}

You can use the credentials provided if you want. It doesn't have any real ether anyway.

Instructions for if you want to look at the entire project...

if you want the entire project links, Github links -

  1. Android studio project
  2. Solidity project link


Once you open and run the android studio project, swipe right on the left side of the screen to open the drawer layout side menu (the navigation button for drawer layout doesn't work yet).
From there, open the contract interface option and then, you'll see the select party card on the top.
That is the function responsible for the voting transactions.
The back end for this card will be found in the directory named ui by the name ContractScreenFragment.
Then, somewhere at the top of that file you can find the onclick listener for the select party to vote card.

Here's what it would look like-

 casteVoteButton.setOnClickListener {
            viewModel.transactionCost.value = transactionInProgress
            CoroutineScope(Dispatchers.IO).launch {
                val cost = voteContractDelegate.casteVote(partyEnum)
                Log.d(TAG, "transaction output:-\n$cost")
                CoroutineScope(Dispatchers.Main).launch {
                    viewModel.transactionCost.value = cost
                }
            }
        }

the rest of the functions have been mentioned above.
For a less complicated code, try checking the test button onclick listener at the end of the same file. That should be easier to understand.
That onclick listener is connected to the bottom card found inside the contract interface. you'll enter the gas price in Gwei in the edit text and then call the same function.

0 Answers0