ABI Formats

There are several formats available to specify an ABI for a Smart Contract, which specifies to the under-lying library what methods, events and errors exist so that encoding and decoding the data from and to the network can be handled by the library.

Human-Readable ABI

The Human-Readable ABI allows for a Solidity signatures to be used to describe each method, event and error.

It is important to note that a Solidity signature fully describes all the properties the ABI requires:

  • name

  • type (constructor, event, function)

  • inputs (types, nested structures and optionally names)

  • outputs (types nested structures and optionally names)

  • state mutability (for constructors and methods)

  • payability (for constructors and methods)

  • whether inputs are indexed (for events)

This allows for a simple format which is both machine-readable (since the parser is a machine) and human-readable (at least developer-readable), as well as simple for humans to type and inline into code, which improves code readability. The Human-Readable ABI is also considerably smaller, which helps reduce code size.

A Human-Readable ABI is simple an array of strings, where each string is the Solidity signature.

Signatures may be minimally specified (i.e. names of inputs and outputs may be omitted) or fully specified (i.e. with all property names) and whitespace is ignored.

Several modifiers available in Solidity are dropped internally, as they are not required for the ABI and used old by Solidity's semantic checking system, such as input parameter data location like "calldata" and "memory". As such, they can be safely dropped in the ABI as well.

const humanReadableAbi = [

  // Simple types
  "constructor(string symbol, string name)",
  "function transferFrom(address from, address to, uint value)",
  "function balanceOf(address owner) view returns (uint balance)",
  "event Transfer(address indexed from, address indexed to, address value)",
  "error InsufficientBalance(account owner, uint balance)",

  // Some examples with the struct type, we use the tuple keyword:
  // (note: the tuple keyword is optional, simply using additional
  //        parentheses accomplishes the same thing)
  // struct Person {
  //   string name;
  //   uint16 age;
  // }
  "function addPerson(tuple(string name, uint16 age) person)",
  "function addPeople(tuple(string name, uint16 age)[] person)",
  "function getPerson(uint id) view returns (tuple(string name, uint16 age))",

  "event PersonAdded(uint indexed id, tuple(string name, uint16 age) person)"
];

Solidity JSON ABI

The Solidity JSON ABI is a standard format that many tools export, including the Solidity compiler. For the full specification, see the Solidity compiler documentation.

Various versions include slightly different keys and values. For example, early compilers included only a boolean "constant" to indicate mutability, while newer versions include a string "mutabilityState", which encompasses several older properties.

When creating an instance of a Fragment using a JSON ABI, it will automatically infer all legacy properties for new-age ABIs and for legacy ABIs will infer the new-age properties. All properties will be populated, so it will match the equivalent created using a Human-Readable ABI fragment.

const jsonAbi = `[
  {
    "type": "constructor",
    "payable": false,
    "inputs": [
      { "type": "string", "name": "symbol" },
      { "type": "string", "name": "name" }
    ]
  },
  {
    "type": "function",
    "name": "transferFrom",
    "constant": false,
    "payable": false,
    "inputs": [
      { "type": "address", "name": "from" },
      { "type": "address", "name": "to" },
      { "type": "uint256", "name": "value" }
    ],
    "outputs": [ ]
  },
  {
    "type": "function",
    "name": "balanceOf",
    "constant":true,
    "stateMutability": "view",
    "payable":false, "inputs": [
      { "type": "address", "name": "owner"}
    ],
    "outputs": [
      { "type": "uint256"}
    ]
  },
  {
    "type": "event",
    "anonymous": false,
    "name": "Transfer",
    "inputs": [
      { "type": "address", "name": "from", "indexed":true},
      { "type": "address", "name": "to", "indexed":true},
      { "type": "address", "name": "value"}
    ]
  },
  {
    "type": "error",
    "name": "InsufficientBalance",
    "inputs": [
      { "type": "account", "name": "owner"},
      { "type": "uint256", "name": "balance"}
    ]
  },
  {
    "type": "function",
    "name": "addPerson",
    "constant": false,
    "payable": false,
    "inputs": [
      {
        "type": "tuple",
        "name": "person",
        "components": [
          { "type": "string", "name": "name" },
          { "type": "uint16", "name": "age" }
        ]
      }
    ],
    "outputs": []
  },
  {
    "type": "function",
    "name": "addPeople",
    "constant": false,
    "payable": false,
    "inputs": [
      {
        "type": "tuple[]",
        "name": "person",
        "components": [
          { "type": "string", "name": "name" },
          { "type": "uint16", "name": "age" }
        ]
      }
    ],
    "outputs": []
  },
  {
    "type": "function",
    "name": "getPerson",
    "constant": true,
    "stateMutability": "view",
    "payable": false,
    "inputs": [
      { "type": "uint256", "name": "id" }
    ],
    "outputs": [
      {
        "type": "tuple",
        "components": [
          { "type": "string", "name": "name" },
          { "type": "uint16", "name": "age" }
        ]
      }
    ]
  },
  {
    "type": "event",
    "anonymous": false,
    "name": "PersonAdded",
    "inputs": [
      { "type": "uint256", "name": "id", "indexed": true },
      {
        "type": "tuple",
        "name": "person",
        "components": [
          { "type": "string", "name": "name", "indexed": false },
          { "type": "uint16", "name": "age", "indexed": false }
        ]
      }
    ]
  }
]`;

Solidity Object ABI

The output from parsing (using JSON.parse) a Solidity JSON ABI is also fully compatible with the Interface class and each method, event and error from that object are compatible with the Fragment class.

Some developers may prefer this as it allows access to the ABI properties as normal JavaScript objects, and closely matches the JSON ABI that those familiar with the Solidity ABI will recognize.

Converting Between Formats

The Fragment object makes it simple to reformat a single method, event or error, however most developers will be interested in converting an entire ABI.

For production code it is recommended to inline the Human-Readable ABI as it makes it easy to see at a glance which methods, events and errors are available. It is also highly recommend to strip out unused parts of the ABI (such as admin methods) to further reduce code size.

// Using the "full" format will ensure the Result objects
// have named properties, which improves code readability
const iface = new Interface(jsonAbi);
iface.format(FormatTypes.full);
// [
//   'constructor(string symbol, string name)',
//   'function transferFrom(address from, address to, uint256 value)',
//   'function balanceOf(address owner) view returns (uint256)',
//   'event Transfer(address indexed from, address indexed to, address value)',
//   'error InsufficientBalance(account owner, uint256 balance)',
//   'function addPerson(tuple(string name, uint16 age) person)',
//   'function addPeople(tuple(string name, uint16 age)[] person)',
//   'function getPerson(uint256 id) view returns (tuple(string name, uint16 age))',
//   'event PersonAdded(uint256 indexed id, tuple(string name, uint16 age) person)'
// ]
// Using the "minimal" format will save a small amount of
// space, but is generally not worth it as named properties
// on Results will not be available
const iface = new Interface(jsonAbi);
iface.format(FormatTypes.minimal);
// [
//   'constructor(string,string)',
//   'function transferFrom(address,address,uint256)',
//   'function balanceOf(address) view returns (uint256)',
//   'event Transfer(address indexed,address indexed,address)',
//   'error InsufficientBalance(account,uint256)',
//   'function addPerson(tuple(string,uint16))',
//   'function addPeople(tuple(string,uint16)[])',
//   'function getPerson(uint256) view returns (tuple(string,uint16))',
//   'event PersonAdded(uint256 indexed,tuple(string,uint16))'
// ]
// Sometimes you may need to export a Human-Readable ABI to
// JSON for tools that do not have Human-Readable support

// For compactness, the JSON is kept with minimal white-space
const iface = new Interface(humanReadableAbi);
jsonAbi = iface.format(FormatTypes.json);
// '[{"type":"constructor","payable":false,"inputs":[{"type":"string","name":"symbol"},{"type":"string","name":"name"}]},{"type":"function","name":"transferFrom","constant":false,"payable":false,"inputs":[{"type":"address","name":"from"},{"type":"address","name":"to"},{"type":"uint256","name":"value"}],"outputs":[]},{"type":"function","name":"balanceOf","constant":true,"stateMutability":"view","payable":false,"inputs":[{"type":"address","name":"owner"}],"outputs":[{"type":"uint256","name":"balance"}]},{"type":"event","anonymous":false,"name":"Transfer","inputs":[{"type":"address","name":"from","indexed":true},{"type":"address","name":"to","indexed":true},{"type":"address","name":"value"}]},{"type":"error","name":"InsufficientBalance","inputs":[{"type":"account","name":"owner"},{"type":"uint256","name":"balance"}]},{"type":"function","name":"addPerson","constant":false,"payable":false,"inputs":[{"type":"tuple","name":"person","components":[{"type":"string","name":"name"},{"type":"uint16","name":"age"}]}],"outputs":[]},{"type":"function","name":"addPeople","constant":false,"payable":false,"inputs":[{"type":"tuple[]","name":"person","components":[{"type":"string","name":"name"},{"type":"uint16","name":"age"}]}],"outputs":[]},{"type":"function","name":"getPerson","constant":true,"stateMutability":"view","payable":false,"inputs":[{"type":"uint256","name":"id"}],"outputs":[{"type":"tuple","components":[{"type":"string","name":"name"},{"type":"uint16","name":"age"}]}]},{"type":"event","anonymous":false,"name":"PersonAdded","inputs":[{"type":"uint256","name":"id","indexed":true},{"type":"tuple","name":"person","components":[{"type":"string","name":"name","indexed":false},{"type":"uint16","name":"age","indexed":false}]}]}]'

// However it is easy to use JSON.parse and JSON.stringify
// with formatting parameters to assist with readability
JSON.stringify(JSON.parse(jsonAbi), null, 2);
// `[
//   {
//     "type": "constructor",
//     "payable": false,
//     "inputs": [
//       {
//         "type": "string",
//         "name": "symbol"
//       },
//       {
//         "type": "string",
//         "name": "name"
//       }
//     ]
//   },
//   {
//     "type": "function",
//     "name": "transferFrom",
//     "constant": false,
//     "payable": false,
//     "inputs": [
//       {
//         "type": "address",
//         "name": "from"
//       },
//       {
//         "type": "address",
//         "name": "to"
//       },
//       {
//         "type": "uint256",
//         "name": "value"
//       }
//     ],
//     "outputs": []
//   },
//   {
//     "type": "function",
//     "name": "balanceOf",
//     "constant": true,
//     "stateMutability": "view",
//     "payable": false,
//     "inputs": [
//       {
//         "type": "address",
//         "name": "owner"
//       }
//     ],
//     "outputs": [
//       {
//         "type": "uint256",
//         "name": "balance"
//       }
//     ]
//   },
//   {
//     "type": "event",
//     "anonymous": false,
//     "name": "Transfer",
//     "inputs": [
//       {
//         "type": "address",
//         "name": "from",
//         "indexed": true
//       },
//       {
//         "type": "address",
//         "name": "to",
//         "indexed": true
//       },
//       {
//         "type": "address",
//         "name": "value"
//       }
//     ]
//   },
//   {
//     "type": "error",
//     "name": "InsufficientBalance",
//     "inputs": [
//       {
//         "type": "account",
//         "name": "owner"
//       },
//       {
//         "type": "uint256",
//         "name": "balance"
//       }
//     ]
//   },
//   {
//     "type": "function",
//     "name": "addPerson",
//     "constant": false,
//     "payable": false,
//     "inputs": [
//       {
//         "type": "tuple",
//         "name": "person",
//         "components": [
//           {
//             "type": "string",
//             "name": "name"
//           },
//           {
//             "type": "uint16",
//             "name": "age"
//           }
//         ]
//       }
//     ],
//     "outputs": []
//   },
//   {
//     "type": "function",
//     "name": "addPeople",
//     "constant": false,
//     "payable": false,
//     "inputs": [
//       {
//         "type": "tuple[]",
//         "name": "person",
//         "components": [
//           {
//             "type": "string",
//             "name": "name"
//           },
//           {
//             "type": "uint16",
//             "name": "age"
//           }
//         ]
//       }
//     ],
//     "outputs": []
//   },
//   {
//     "type": "function",
//     "name": "getPerson",
//     "constant": true,
//     "stateMutability": "view",
//     "payable": false,
//     "inputs": [
//       {
//         "type": "uint256",
//         "name": "id"
//       }
//     ],
//     "outputs": [
//       {
//         "type": "tuple",
//         "components": [
//           {
//             "type": "string",
//             "name": "name"
//           },
//           {
//             "type": "uint16",
//             "name": "age"
//           }
//         ]
//       }
//     ]
//   },
//   {
//     "type": "event",
//     "anonymous": false,
//     "name": "PersonAdded",
//     "inputs": [
//       {
//         "type": "uint256",
//         "name": "id",
//         "indexed": true
//       },
//       {
//         "type": "tuple",
//         "name": "person",
//         "components": [
//           {
//             "type": "string",
//             "name": "name",
//             "indexed": false
//           },
//           {
//             "type": "uint16",
//             "name": "age",
//             "indexed": false
//           }
//         ]
//       }
//     ]
//   }
// ]`

Last updated