mhw0/libethc

Sign Contract

DerXanRam opened this issue ยท 28 comments

Hello bro. I am trying to sign this contract on Uniswapv2 router 02

function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
        external
        payable
        returns (uint[] memory amounts);

and i write thos code....

struct eth_abi abi;
	char *fn = "swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)", 
	*addr = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", *hex;
	char *weth="0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
	char *usdt="0xdAC17F958D2ee523a2206206994597C13D831ec7";
	char *myaddress="0xa9d0a7dC416f586491f2fb596731598F937617b5";
	size_t hexlen;

	ok(eth_abi_init(&abi, ETH_ABI_ENCODE));

	ok(eth_abi_call(&abi, &fn, NULL)); 
	ok(eth_abi)
	ok(eth_abi_init(&abi, 0));
	ok(eth_abi_array(&abi, ETH_ABI_ENCODE));
	  ok(eth_abi_address(&abi,&weth));
	  ok(eth_abi_address(&abi,&usdt));
	ok(eth_abi_array_end(&abi));
	/*i try to add string "account" but i don't know how*/
	ok(eth_abi_call_end(&abi));	

	ok(eth_abi_to_hex(&abi, &hex, &hexlen));
	ok(eth_abi_free(&abi));
	printf("encoded abi: %s\n", hex);
	free(hex);

But as u can see the comment found in the code, i have hard time to pass string to the ABI. and also where should i add the contract address?

If u can, can u show me how i sign this contract correctly and to send to the RPC-API eth_sendRawTransaction?plsss

mhw0 commented

Hello. If the signature of the function is swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline), it should be like this (not tested):

eth_abi_init(&abi, ETH_ABI_ENCODE)

eth_abi_call(&abi, &func_name, NULL);
  eth_abi_uint256(&abi, &amount_out_min); 

  eth_abi_array(&abi); // open an array
    eth_abi_address(&abi, &address0);
    eth_abi_address(&abi, &address1);
    // 2 3 4 ...
  eth_abi_array_end(&abi); // close the array

  eth_abi_address(&abi, &to_address);
  eth_abi_uint256(&abi, &deadline);
eth_abi_call_end(&abi);

eth_abi_free(&abi);

Now, just replace variables with yours and it should work.

mhw0 commented

And, you also need to remove whitespaces and parameter names from the function signature. Replace it with this: swapExactETHForTokens(uint,address[],address,uint)

Hello. If the signature of the function is swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline), it should be like this (not tested):

eth_abi_init(&abi, ETH_ABI_ENCODE)

eth_abi_call(&abi, &func_name, NULL);
  eth_abi_uint256(&abi, &amount_out_min); 

  eth_abi_array(&abi); // open an array
    eth_abi_address(&abi, &address0);
    eth_abi_address(&abi, &address1);
    // 2 3 4 ...
  eth_abi_array_end(&abi); // close the array

  eth_abi_address(&abi, &to_address);
  eth_abi_uint256(&abi, &deadline);
eth_abi_call_end(&abi);

eth_abi_free(&abi);

Now, just replace variables with yours and it should work. and can fill "account" in eth_abi_address(&abi, &to_address);?

ok but where do i pass the contract address?

mhw0 commented

What is that address for? Are you trying to send from that address?

mhw0 commented

Oh, I see what you mean. Take a look at this example: https://github.com/mhw0/libethc/blob/main/examples/legacy-transaction.c. Just replace the toaddr with your contract address, and the data field with the encoded ABI (don't forget about chainid and other things)

What is that address for? Are you trying to send from that address?

What i want to do is send the signed data by curl and sign uniswap contract... i.e like this

curl https://eth-mainnet.blastapi.io/<project-id> \
-X POST \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"],"id":1}'

Here is the API doc

But without the Uniswap's contract address i think the network can not find the ABI or did i miss the concept? Sorry bro i am new for blockchain...

mhw0 commented

Yeah, I should definitely add examples for contract calls.

This line and this line are data fields. After you get the encoded ABI, replace those with ok(eth_rlp_hex(&rlp1, &abi_result)); and also replace other fields like chainid, gaslimit, toaddr etc. It should be it.

like this....

 ok(eth_rlp_init(&rlp0, ETH_RLP_ENCODE));

  ok(eth_rlp_array(&rlp0));                  // [
    ok(eth_rlp_uint8(&rlp0, &nonce));        //   0x00,
    ok(eth_rlp_hex(&rlp0, &gasprice, NULL)); //   0x04a817c800, 
    ok(eth_rlp_hex(&rlp0, &gaslimit, NULL)); //   0x5208,
    ok(eth_rlp_address(&rlp0, &toaddr));     //   0x3535353535353535353535353535353535353535,
    ok(eth_rlp_hex(&rlp0, &value, NULL));    //   0x0de0b6b3a7640000,
    ok(eth_rlp_hex(&rlp1, &abi_result));        //   0x,  //replaced
    ok(eth_rlp_uint16(&rlp0, &chainid));     //   0x7a69,
    ok(eth_rlp_uint8(&rlp0, &zero));         //   0x,
    ok(eth_rlp_uint8(&rlp0, &zero));         //   0x,
  ok(eth_rlp_array_end(&rlp0));              // ]

  ok(eth_rlp_to_bytes(&rlp0bytes, &rlp0len, &rlp0));
  ok(eth_rlp_free(&rlp0));

  // compute the keccak hash of the encoded rlp elements
  ok(eth_keccak256(keccak, rlp0bytes, rlp0len));
  free(rlp0bytes);

  // sign the transaction
  ok(eth_ecdsa_sign(&sign, privkey, keccak));

  // calculate v
  v = sign.recid + chainid * 2 + 35;
  r = sign.r;
  s = sign.s;

  ok(eth_rlp_init(&rlp1, ETH_RLP_ENCODE));

  ok(eth_rlp_array(&rlp1));
    ok(eth_rlp_uint8(&rlp1, &nonce));
    ok(eth_rlp_hex(&rlp1, &gasprice, NULL));
    ok(eth_rlp_hex(&rlp1, &gaslimit, NULL));
    ok(eth_rlp_address(&rlp1, &toaddr));
    ok(eth_rlp_hex(&rlp1, &value, NULL));
    ok(eth_rlp_hex(&rlp1, &abi_result));           //replaced
    ok(eth_rlp_uint16(&rlp1, &v));
    ok(eth_rlp_bytes(&rlp1, &r, &siglen));
    ok(eth_rlp_bytes(&rlp1, &s, &siglen));
  ok(eth_rlp_array_end(&rlp1));

and passing Uniswap's contract address by toaddr. Right??

mhw0 commented

Yeah, looks right.

Ok wait...please don't close the issue....let me test it......

Hello. If the signature of the function is swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline), it should be like this (not tested):

eth_abi_init(&abi, ETH_ABI_ENCODE)

eth_abi_call(&abi, &func_name, NULL);
  eth_abi_uint256(&abi, &amount_out_min); 

  eth_abi_array(&abi); // open an array
    eth_abi_address(&abi, &address0);
    eth_abi_address(&abi, &address1);
    // 2 3 4 ...
  eth_abi_array_end(&abi); // close the array

  eth_abi_address(&abi, &to_address);
  eth_abi_uint256(&abi, &deadline);
eth_abi_call_end(&abi);

eth_abi_free(&abi);

Now, just replace variables with yours and it should work. and can fill "account" in eth_abi_address(&abi, &to_address);?

ok but where do i pass the contract address?

It is throwing error identifier "eth_abi_uint256" is undefined at this code
eth_abi_uint256(&abi, &amount_out_min);

And "too few argument" error at this code
eth_abi_array(&abi); // open an array

mhw0 commented

It is throwing error at the ABI function
"identifier "eth_abi_uint256" is undefined"

Oh, there is no function called eth_abi_uint256. I totally forgot. Replace them with eth_abi_uint64. Hopefully amountOutMin and deadline fit in uint64

And "too few argument" error at this code
eth_abi_array(&abi); // open an array

eth_abi_array alco accepts the length of the array, So, specify the length of the path parameter (eth_abi_array(&abi, &path_len))

path_len

does path_len an uint64_t type which indicates number of elements in the array?

mhw0 commented

path_len

does path_len an intiger type which indicates number of elements in the array?

Yes, it's uint64_t

and bro what is "len" parameter indicates in this function. Very sorry for nagging u ๐Ÿ™ ....because it is throwing "too few arguments" error.
eth_rlp_hex(&rlp1, &abi_result));

mhw0 commented

No, it's all good. That's the length of the hex parameter. You don't have to specify the value if your string is NUL terminated. So, just pass NULL (eth_rlp_hex(&rlp1, &abi_result, NULL))

man again another error. the passed ABI hex data is
c033b18e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000014aae8060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700

which the lats 00 is hex null termination. It runs But send this message
Assertion `eth_rlp_hex(&rlp1, &abi_result,__null) >= 0' failed.

mhw0 commented

Can you please send the whole code? (Please don't leak private key)

char* SignTransaction()
{
	char *abi_result="c033b18e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000014aae8060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700";
	struct eth_rlp rlp0, rlp1;
	struct eth_ecdsa_signature sign;
	uint8_t privkey[] = {0xdf, 0x57, 0x08, 0x9f, 0xeb, 0xba, 0xcf, 0x7b,
						 0xa0, 0xbc, 0x22, 0x7d, 0xaf, 0xbf, 0xfa, 0x9f,
						 0xc0, 0x8a, 0x93, 0xfd, 0xc6, 0x8e, 0x1e, 0x42,
						 0x41, 0x1a, 0x14, 0xef, 0xcf, 0x23, 0x65, 0x6e};

	uint8_t nonce = 0x00, zero = 0x00, keccak[32], *rlp0bytes, *r, *s;
	char *gasprice = "0x04a817c800", *gaslimit = "0x5208", *value = "0x0de0b6b3a7640000";
	char *toaddr = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", *txn;
	uint16_t chainid = 0x1, v;
	size_t rlp0len, rlp1len, siglen = 32;

	ok(eth_rlp_init(&rlp0, ETH_RLP_ENCODE));

	ok(eth_rlp_array(&rlp0));				 // [
	ok(eth_rlp_uint8(&rlp0, &nonce));		 //   0x00,
	ok(eth_rlp_hex(&rlp0, &gasprice, NULL)); //   0x04a817c800,
	ok(eth_rlp_hex(&rlp0, &gaslimit, NULL)); //   0x5208,
	ok(eth_rlp_address(&rlp0, &toaddr));	 //   0x3535353535353535353535353535353535353535,
	//ok(eth_rlp_hex(&rlp0, &value, NULL));	 //   0x0de0b6b3a7640000,
	ok(eth_rlp_hex(&rlp1, &abi_result,NULL));		 //replaced
	ok(eth_rlp_uint16(&rlp0, &chainid));	 //   0x7a69,
	//ok(eth_rlp_uint8(&rlp0, &zero));		 //   0x,
	//ok(eth_rlp_uint8(&rlp0, &zero));		 //   0x,
	ok(eth_rlp_array_end(&rlp0));			 // ]

	ok(eth_rlp_to_bytes(&rlp0bytes, &rlp0len, &rlp0));
	ok(eth_rlp_free(&rlp0));

	// compute the keccak hash of the encoded rlp elements
	ok(eth_keccak256(keccak, rlp0bytes, rlp0len));
	free(rlp0bytes);

	// sign the transaction
	ok(eth_ecdsa_sign(&sign, privkey, keccak));

	// calculate v
	v = sign.recid + chainid * 2 + 35;
	r = sign.r;
	s = sign.s;

	ok(eth_rlp_init(&rlp1, ETH_RLP_ENCODE));

	ok(eth_rlp_array(&rlp1));
	ok(eth_rlp_uint8(&rlp1, &nonce));
	ok(eth_rlp_hex(&rlp1, &gasprice, NULL));
	ok(eth_rlp_hex(&rlp1, &gaslimit, NULL));
	ok(eth_rlp_address(&rlp1, &toaddr));
	ok(eth_rlp_hex(&rlp1, &value, NULL));
	ok(eth_rlp_hex(&rlp1, &abi_result, NULL)); //replaced
	ok(eth_rlp_uint16(&rlp1, &v));
	ok(eth_rlp_bytes(&rlp1, &r, &siglen));
	ok(eth_rlp_bytes(&rlp1, &s, &siglen));
	ok(eth_rlp_array_end(&rlp1));

	ok(eth_rlp_to_hex(&txn, &rlp1));
	ok(eth_rlp_free(&rlp1));

	return txn;
}
mhw0 commented

Thank you. Give me some time please. I'll try myself.

ok bro....i will wait ๐Ÿ‘

mhw0 commented

here is the code with fixes:

int main(void) {
        char *abi_result="c033b18e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000014aae8060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700";
        struct eth_rlp rlp0, rlp1;
        struct eth_ecdsa_signature sign;
        uint8_t privkey[] = {0xdf, 0x57, 0x08, 0x9f, 0xeb, 0xba, 0xcf, 0x7b,
                                                 0xa0, 0xbc, 0x22, 0x7d, 0xaf, 0xbf, 0xfa, 0x9f,
                                                 0xc0, 0x8a, 0x93, 0xfd, 0xc6, 0x8e, 0x1e, 0x42,
                                                 0x41, 0x1a, 0x14, 0xef, 0xcf, 0x23, 0x65, 0x6e};

        uint8_t nonce = 0x00, zero = 0x00, keccak[32], *rlp0bytes, *r, *s;
        char *gasprice = "0x04a817c800", *gaslimit = "0x5208", *value = "0x0de0b6b3a7640000";
        char *toaddr = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", *txn;
        uint16_t chainid = 0x1, v;
        size_t rlp0len, rlp1len, siglen = 32;

        ok(eth_rlp_init(&rlp0, ETH_RLP_ENCODE));

        ok(eth_rlp_array(&rlp0));                                // [
        ok(eth_rlp_uint8(&rlp0, &nonce));                //   0x00,
        ok(eth_rlp_hex(&rlp0, &gasprice, NULL)); //   0x04a817c800,
        ok(eth_rlp_hex(&rlp0, &gaslimit, NULL)); //   0x5208,
        ok(eth_rlp_address(&rlp0, &toaddr));     //   0x3535353535353535353535353535353535353535,
        ok(eth_rlp_hex(&rlp0, &value, NULL));    //   0x0de0b6b3a7640000,
        // FIX1: it was rlp1, changed it to rlp0
        ok(eth_rlp_hex(&rlp0, &abi_result, NULL));               //replaced
        ok(eth_rlp_uint16(&rlp0, &chainid));     //   0x7a69,
        // FIX2: do not comment these
        ok(eth_rlp_uint8(&rlp0, &zero));                 //   0x,
        ok(eth_rlp_uint8(&rlp0, &zero));                 //   0x,
        ok(eth_rlp_array_end(&rlp0));                    // ]

        ok(eth_rlp_to_bytes(&rlp0bytes, &rlp0len, &rlp0));
        ok(eth_rlp_free(&rlp0));

        // compute the keccak hash of the encoded rlp elements
        ok(eth_keccak256(keccak, rlp0bytes, rlp0len));
        free(rlp0bytes);

        // sign the transaction
        ok(eth_ecdsa_sign(&sign, privkey, keccak));

        // calculate v
        v = sign.recid + chainid * 2 + 35;
        r = sign.r;
        s = sign.s;

        ok(eth_rlp_init(&rlp1, ETH_RLP_ENCODE));

        ok(eth_rlp_array(&rlp1));
        ok(eth_rlp_uint8(&rlp1, &nonce));
        ok(eth_rlp_hex(&rlp1, &gasprice, NULL));
        ok(eth_rlp_hex(&rlp1, &gaslimit, NULL));
        ok(eth_rlp_address(&rlp1, &toaddr));
        ok(eth_rlp_hex(&rlp1, &value, NULL));
        ok(eth_rlp_hex(&rlp1, &abi_result, NULL)); //replaced
        ok(eth_rlp_uint16(&rlp1, &v));
        ok(eth_rlp_bytes(&rlp1, &r, &siglen));
        ok(eth_rlp_bytes(&rlp1, &s, &siglen));
        ok(eth_rlp_array_end(&rlp1));

        // FIX3: this actually returns the output length
        ok(eth_rlp_to_hex(&txn, &rlp1) > 0);

        printf("raw transaction: %s\n", txn);
        ok(eth_rlp_free(&rlp1));

        return 0;
}

testing....

Thanks very much. it generate the hash but the RPC-API response is not good.. But I will solve this by my self ๐Ÿ‘
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"rlp: unknown tx type prefix, got: 48"}}

But i have some questions. in the corrected code what is rlp0 indicates?and what are those uncommeneted "zero"s?

mhw0 commented

But i have some questions. in the corrected code what is the difference between rlp1 and rlp0 ?

Currently, you can't replace RLP values (but it would be cool). So, you have to create two separate RLP instances for that.

and what are those uncommeneted "zero"s?

They're required values and must be included in RLP even if they're emtpy. From EIP-155: (nonce, gasprice, startgas, to, value, data, chainid, 0, 0)

Also, you have to change chain id, nonce, gas limit, gas price and value fields (value should be 0 in your case)

Please let me know if you need any help with this library :)

mhw0 commented

And, please, consider adding the code to the examples if it works. It would be very useful for others.

I will add after it worked bro. Thanks very much ๐Ÿ™