Skip to content

Issues With Memory String Consumption In Smart Contracts #80

@baxterjfinch

Description

@baxterjfinch

When attempting to consume a String in a contract function, ExW3 seems to handle encoding improperly, causing variables to mix and or set improperly in the contracts state.

Smart Contract Example:

pragma solidity >=0.4.22 <0.9.0;

contract Tester {
  address public currentAddr;
  uint256 public currentInt;
  string public sym;

  function setOnlyStringBefore(string memory _sym, address new_addr, uint256 new_int) public {
    currentAddr = new_addr;
    currentInt = new_int;
    sym = _sym;
  }

  function setOnlyStringAfter(address new_addr, uint256 new_int, string memory _sym) public {
    currentAddr = new_addr;
    currentInt = new_int;
    sym = _sym;
  }

  function setOnlyAddrAndInt(address new_addr, uint256 new_int) public {
    currentAddr = new_addr;
    currentInt = new_int;
  }
}

Scenario 1

(Function setOnlyStringBefore) - string is set before address and uint256 in smart contract function parameters

Using ExW3 ->
ExW3.Contract.send(:Tester, :setOnlyStringBefore, ["name", "0x0255bff90b8787f06cf13ab325997cbf3b139c1d", 10], %{from: @contracts_owner, gas: 6000000})

{:ok, "0x1c6ac0864e8e6418cfc743200ee74ad06bb2442bd5842fd73c2d12bb835db0dc"}

In Truffle ->

truffle(k8s)> let test = await Tester.deployed()

truffle(k8s)> test.sym()

Thrown:
{ Error: invalid codepoint at offset 14; unexpected continuation byte (argument="bytes", value={"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":2,"13":85,"14":191,"15":249,"16":11,"17":135,"18":135,"19":240,"20":108,"21":241,"22":58,"23":179,"24":37,"25":153,"26":124,"27":191,"28":59,"29":19,"30":156,"31":29,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":10,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0,"79":0,"80":0,"81":0,"82":0,"83":0,"84":0,"85":0,"86":0,"87":0,"88":0,"89":0,"90":0,"91":0,"92":0,"93":0,"94":0,"95":4}, code=INVALID_ARGUMENT, version=strings/5.0.5)
    at evalmachine.<anonymous>:0:6
    at sigintHandlersWrap (vm.js:288:15)
    at Script.runInContext (vm.js:130:14)
    at runScript (/usr/local/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:251:1)
    at Console.interpret (/usr/local/lib/node_modules/truffle/build/webpack:/packages/core/lib/console.js:266:1)
    at bound (domain.js:402:14)
    at REPLServer.runBound [as eval] (domain.js:415:12)
    at REPLServer.onLine (repl.js:642:10)
    at REPLServer.emit (events.js:198:13)
    at REPLServer.EventEmitter.emit (domain.js:448:20)
    at REPLServer.Interface._onLine (readline.js:308:10)
    at REPLServer.Interface._line (readline.js:656:8)
    at REPLServer.Interface._ttyWrite (readline.js:937:14)
    at REPLServer.self._ttyWrite (repl.js:715:7)
    at ReadStream.onkeypress (readline.js:184:10)
    at ReadStream.emit (events.js:198:13)
    at ReadStream.EventEmitter.emit (domain.js:448:20)
    at emitKeys (internal/readline.js:424:14)
    at emitKeys.next (<anonymous>)
    at ReadStream.onData (readline.js:1073:36)
    at ReadStream.emit (events.js:198:13)
    at ReadStream.EventEmitter.emit (domain.js:448:20)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
    at ReadStream.Readable.push (_stream_readable.js:224:10)
    at TTY.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
  reason:
   'invalid codepoint at offset 14; unexpected continuation byte',
  code: 'INVALID_ARGUMENT',
  argument: 'bytes',
  value:
   Uint8Array [
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     2,
     85,
     191,
     249,
     11,
     135,
     135,
     240,
     108,
     241,
     58,
     179,
     37,
     153,
     124,
     191,
     59,
     19,
     156,
     29,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     10,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     0,
     4 ],
  baseType: 'string',
  name: null,
  type: 'string',
  hijackedStack:
   'Error: invalid codepoint at offset 14; unexpected continuation byte (argument="bytes", value={"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":2,"13":

truffle(k8s)> test.currentAddr()
'0x0000000000000000000000000000000000000060'

truffle(k8s)> test.currentInt()
<BN: 255bff90b8787f06cf13ab325997cbf3b139c1d>

Note: The transaction is successful but the sym is set as a byte array and the address and uint256 values are swapped. They are passed in as

[name, new_addr, new_int]

and the function consumes them as

(string memory _sym, address new_addr, uint256 new_int)

however you can see in the above .currentAddr() and .currentInt() that the addr is set as a hex encoded value of 10 and the int is set as a BigNumber conversion of the address.

Note: This function is successful when called directly from truffle

truffle(k8s)> test.setOnlyStringBefore("Name", "0xfe5e669eD4C62A7D4621A604BcC1029171814046", 10)
{ tx:
   '0x1e7f54778810d2ea33b604dc2e1791b05b69540cd77a2357e8572f3f3f580588',
  receipt:
   { blockHash:
      '0x28a3a821daf8b6d8844b330f355ac74e46ee46c900f1be5b748c166357b044c2',
     blockNumber: 878,
     contractAddress: null,
     cumulativeGasUsed: 85007,
     from: '0xfe5e669ed4c62a7d4621a604bcc1029171814046',
     gasUsed: 85007,
     logs: [],
     logsBloom:
      '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
     status: true,
     to: '0x3c7d058ccb934c732674e28b4c45bcc722fb0004',
     transactionHash:
      '0x1e7f54778810d2ea33b604dc2e1791b05b69540cd77a2357e8572f3f3f580588',
     transactionIndex: 0,
     rawLogs: [] },
  logs: [] }

truffle(k8s)> test.sym()

'Name'

truffle(k8s)> test.currentAddr()

'0xfe5e669eD4C62A7D4621A604BcC1029171814046'

truffle(k8s)> test.currentInt()

<BN: a>


Scenerio 2

### (Function setOnlyStringAfter) - string is set after address and uint256 in smart contract function parameters

ExW3.Contract.send(:Tester, :setOnlyStringAfter, [10, "0x0255bff90b8787f06cf13ab325997cbf3b139c1d", "name"], %{from: @contracts_owner, gas: 6000000})

{:ok, "0xcb1e0f29d7a0fa5bc7822c2d9302b12cc5a22b02a52323d3ac3bad1a01b64634"}

In Truffle ->

truffle(k8s)> let test = await Tester.deployed()

truffle(k8s)> test.sym()

'' //// <--- Value never got set

truffle(k8s)> web3.eth.getTransactionReceipt("0xcb1e0f29d7a0fa5bc7822c2d9302b12cc5a22b02a52323d3ac3bad1a01b64634")
{ blockHash:
   '0x146459c31989cc9cfe8ae9025c31701566b5a3da14b74f8e69b97bfe4290d983',
  blockNumber: 877,
  contractAddress: null,
  cumulativeGasUsed: 24203,
  from: '0xfe5e669ed4c62a7d4621a604bcc1029171814046',
  gasUsed: '0x5e8b',
  logs: [],
  logsBloom:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  status: false,
  to: '0x3c7d058ccb934c732674e28b4c45bcc722fb0004',
  transactionHash:
   '0xcb1e0f29d7a0fa5bc7822c2d9302b12cc5a22b02a52323d3ac3bad1a01b64634',
  transactionIndex: 0 }

Note: The transaction status is 'false', meaning that the transaction has failed for some reason.

Note: The function call is successful when called from Truffle

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions