CS8803: Exploiting Smart Contract and DeFi

Taesoo Kim



CS8803: Solidity 101

Taesoo Kim

Recap

This Week’s Study

Making Raw Transaction

> a0 = eth.accounts[0]
> a1 = eth.accounts[0]
> tx = eth.sign_transaction({
          "from": a0, "to": a1, "value": web3.toWei(1, "gwei"), 
          // recommended gas price of the network, 1000000007 wei (~= 1 gwei)
          "gasPrice": eth.gas_price,
          // > base fee of 21,000
          "gas": 25000,
          // #trasnactions sent so far
          "nonce": eth.get_transaction_count(a0)
       })

Making Raw Transaction

> tx.tx
{ // what we provided
  to: "0x5e3fc8f77d1c499c7c178d3efac6f62a1f9c669e",
  value: "0x3b9aca00", gas: "0x61a8", gasPrice: "0x3b9aca07", nonce: "0x6",

  // empty fields
  input: "0x", 
  maxFeePerGas: null,
  maxPriorityFeePerGas: null,
  
  // hash = keccat(tx.raw), (r, s, v) = a transaction's signature
  hash: "0x33a4fca9102b531b23b16b53e845f22ba58dcde0bf50c38a3b2999c240bedd6c",
  r: "0xcf20b1cfc8eb1d441e64c1db2bde01bdfcdf7500d86fcfef788cfcfe96e15596",
  s: "0x5d117bc559fc4a55a19785d4fa1ef97f6e666cfda892b12331b33a9ded4dbb0b",
  v: "0xa96",

  // indicating that it's a legacy transaction
  type: "0x0" }

Sending Raw Transaction

> tx = eth.send_raw_transaction(raw)
> eth.get_transaction_receipt(tx)
...

Decoding Raw Transaction

> tx.raw
"0xf86906843b9aca078261a8945e3fc8f77d1c499c7c178d3efac6f62a1f9c669e
   843b9aca0080820a96a0cf20b1cfc8eb1d441e64c1db2bde01bdfcdf7500d86f
   cfef788cfcfe96e15596a05d117bc559fc4a55a19785d4fa1ef97f6e666cfda8
   92b12331b33a9ded4dbb0b"
> len(tx.raw)
216

RLP Encoding

Example: RLP Encoding

> import rlp

> rlp.encode(b"\x00")
0x00
> rlp.encode(b"\x80")
0x81 80
> rlp.encode(b"\x01"*56)
0xb838 010101...01

> rlp.encode([b"\x01", b"\x02"])
0xc2 01 02
# 0xc2: 0xc2 - 0xc0 = 2-bytes (list)
> rlp.encode([b"\x01"*56, b"\x02"])
0xf83b b838010101...01 02
# 0xf8-0xf7 = 1 -> 0x3b bytes (list)

Example: RLP Encoding of a Transaction

> rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])
[]      : f869          // 0xf8-0xf7 = 1-byte, 0x69 = 105 bytes
nonce   : 06            // a single byte [0, 0x7f]
gasPrice: 84 3b9aca07   // 4-byte: 0x84 - 0x80
gasLimit: 82 61a8       // 2-byte: 0x82 - 0x80
to      : 94 5e3fc8f77d1c499c7c178d3efac6f62a1f9c669e
value   : 84 3b9aca00
data    : 80            // 0-byte (nil)
v       : 82 0a96       // 2710 = 1337 (chain id) * 2 + 36
r       : a0 cf20b1cfc8eb1d441e64c1db2bde01bdfcdf7500d86fcfef788cfcfe96e15596
s       : a0 5d117bc559fc4a55a19785d4fa1ef97f6e666cfda892b12331b33a9ded4dbb0b"

RLP Encoding in Python

import rlp

nonce    = 6
gasprice = 0x3b9aca07
to       = 0x5e3fc8f77d1c499c7c178d3efac6f62a1f9c669e
value    = 0x3b9aca00
data     = ""
gaslimit = 0x61a8
hash     = 0x33a4fca9102b531b23b16b53e845f22ba58dcde0bf50c38a3b2999c240bedd6c
r        = 0xcf20b1cfc8eb1d441e64c1db2bde01bdfcdf7500d86fcfef788cfcfe96e15596
s        = 0x5d117bc559fc4a55a19785d4fa1ef97f6e666cfda892b12331b33a9ded4dbb0b
chainid  = 1337
v        = 1337*2 + 36

rlp.encode([nonce, gasprice, gaslimit, to, value, data, v, r, s])

Decoding Transaction (Online Toolkit)

Signing a Raw Transaction

signing_data = rlp.encode(
    [nonce, gasprice, gaslimit, to, value, data, chainid, 0, 0])
signing_data_hash = web3.keccak(primitive=signing_data)

sk = PrivateKey(...)
sig = sk.sign_msg_hash(signing_data_hash)

v = sig.v + chainid*2 + 35
r = sig.r
s = sig.s

raw = rlp.encode([nonce, gasprice, gaslimit, to, value, data, v, r, s])
print(HexBytes(raw).hex())

→ You can construct and send a raw transaction!

Atomicity of Transaction

Ref. EI §1

Try Replying a Raw Transaction

> tx = eth.send_raw_transaction(raw)
> eth.get_transaction_receipt(tx)
...

> tx = eth.send_raw_transaction(raw)
> eth.get_transaction_receipt(tx)
...
ValueError: {'code': -32000, 'message': 'nonce too low'}

Order of Transactions

Ref. EI §1

Order of Transactions

Ref. EI §1

Ordering Inner Blocks

Ref. EI §1

Example: Front-running

txs = []
for i in range(10):
    txs.append(send_transaction({"from": a0, "to": a1, "value": 1 gwei}))

# is this going to be accepted first? (a0 to a1)
txs.append(send_transaction(
  {"from": a0, "to": a1, "value": 1 gwei, "gasPrice": eth.gas_price*100}))

# is this going to be accepted first? (a1 to a0)
txs.append(send_transaction(
  {"from": a1, "to": a0, "value": 1 gwei, "gasPrice": eth.gas_price*100}))

# 11? vs 0?
web3.eth.get_transaction_receipt(txs[-1]).transactionIndex

Ordering Inter Blocks

Ref. EI §1

Ethereum Layers

Ref. EI §2

PL for Smart Contract: Solidity

Let’s Implement Some Contracts!

Lab02!

References