ABI Method
Description
Section titled “Description”This example demonstrates how to work with ABI methods:
- Parsing method signatures with ABIMethod.fromSignature()
- Accessing method name, args, and return type
- Generating 4-byte method selectors with getSelector()
- Understanding the relationship: selector = first 4 bytes of SHA-512/256(signature) ABI method signatures follow the pattern: name(arg1Type,arg2Type,…)returnType Examples: ‘transfer(address,uint64)uint64’, ‘hello(string)string’
Prerequisites
Section titled “Prerequisites”- No LocalNet required
Run This Example
Section titled “Run This Example”From the repository root:
cd examplesnpm run example abi/11-abi-method.ts/** * Example: ABI Method * * This example demonstrates how to work with ABI methods: * - Parsing method signatures with ABIMethod.fromSignature() * - Accessing method name, args, and return type * - Generating 4-byte method selectors with getSelector() * - Understanding the relationship: selector = first 4 bytes of SHA-512/256(signature) * * ABI method signatures follow the pattern: name(arg1Type,arg2Type,...)returnType * Examples: 'transfer(address,uint64)uint64', 'hello(string)string' * * Prerequisites: * - No LocalNet required */
import { ABIMethod } from '@algorandfoundation/algokit-utils/abi'import crypto from 'crypto'import { formatHex, printHeader, printInfo, printStep, printSuccess } from '../shared/utils.js'
function main() { printHeader('ABI Method Example')
// Step 1: Introduction to ABI Methods printStep(1, 'Introduction to ABI Methods')
printInfo('ABI methods define the interface for smart contract functions.') printInfo('Method signatures follow the pattern: name(argTypes...)returnType') printInfo('') printInfo('Key components:') printInfo(' - name: The method name (e.g., "transfer")') printInfo(' - args: Array of argument types (e.g., [address, uint64])') printInfo(' - returns: The return type (e.g., uint64)') printInfo(' - selector: 4-byte identifier = SHA-512/256(signature)[0:4]')
// Step 2: Parse a basic method signature printStep(2, 'Parsing Method Signatures')
const transferMethod = ABIMethod.fromSignature('transfer(address,uint64)uint64')
printInfo(`Signature: transfer(address,uint64)uint64`) printInfo(` name: ${transferMethod.name}`) printInfo(` args count: ${transferMethod.args.length}`)
for (let i = 0; i < transferMethod.args.length; i++) { const arg = transferMethod.args[i] printInfo(` args[${i}].type: ${arg.type.toString()}`) }
printInfo(` returns.type: ${transferMethod.returns.type.toString()}`)
// Step 3: Parse various method signatures printStep(3, 'Various Method Signatures')
const helloMethod = ABIMethod.fromSignature('hello(string)string') printInfo(`hello(string)string:`) printInfo(` name: ${helloMethod.name}`) printInfo(` args: [${helloMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${helloMethod.returns.type.toString()}`)
const addMethod = ABIMethod.fromSignature('add(uint64,uint64)uint64') printInfo(`\nadd(uint64,uint64)uint64:`) printInfo(` name: ${addMethod.name}`) printInfo(` args: [${addMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${addMethod.returns.type.toString()}`)
const swapMethod = ABIMethod.fromSignature('swap(address,address,uint64,uint64)bool') printInfo(`\nswap(address,address,uint64,uint64)bool:`) printInfo(` name: ${swapMethod.name}`) printInfo(` args: [${swapMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${swapMethod.returns.type.toString()}`)
// Step 4: Method with no args printStep(4, 'Method with No Arguments')
const getMethod = ABIMethod.fromSignature('get()uint64')
printInfo(`Signature: get()uint64`) printInfo(` name: ${getMethod.name}`) printInfo(` args count: ${getMethod.args.length}`) printInfo(` args: []`) printInfo(` returns.type: ${getMethod.returns.type.toString()}`)
const getCounterMethod = ABIMethod.fromSignature('getCounter()uint256')
printInfo(`\nSignature: getCounter()uint256`) printInfo(` name: ${getCounterMethod.name}`) printInfo(` args: []`) printInfo(` returns: ${getCounterMethod.returns.type.toString()}`)
// Step 5: Method with void return printStep(5, 'Method with Void Return')
const setMethod = ABIMethod.fromSignature('set(uint64)void')
printInfo(`Signature: set(uint64)void`) printInfo(` name: ${setMethod.name}`) printInfo(` args: [${setMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns.type: ${setMethod.returns.type.toString()}`)
const initializeMethod = ABIMethod.fromSignature('initialize(address,string,uint64)void')
printInfo(`\nSignature: initialize(address,string,uint64)void`) printInfo(` name: ${initializeMethod.name}`) printInfo(` args: [${initializeMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${initializeMethod.returns.type.toString()}`)
// Step 6: Method selectors printStep(6, 'Method Selectors (getSelector)')
printInfo('The method selector is a 4-byte identifier used in ABI calls.') printInfo('It is computed as the first 4 bytes of SHA-512/256(signature).') printInfo('')
const methods = [ ABIMethod.fromSignature('transfer(address,uint64)uint64'), ABIMethod.fromSignature('hello(string)string'), ABIMethod.fromSignature('get()uint64'), ABIMethod.fromSignature('set(uint64)void'), ]
for (const method of methods) { const selector = method.getSelector() printInfo(`${method.getSignature()}`) printInfo(` selector: ${formatHex(selector)}`) }
// Step 7: Demonstrate selector computation printStep(7, 'Selector Computation Deep Dive')
const demoMethod = ABIMethod.fromSignature('transfer(address,uint64)uint64') const signature = demoMethod.getSignature()
printInfo(`Signature: ${signature}`) printInfo('') printInfo('Computing selector:') printInfo(' 1. Take the method signature string') printInfo(' 2. Compute SHA-512/256 hash of the signature') printInfo(' 3. Take the first 4 bytes as the selector') printInfo('')
// Compute the hash manually to show the relationship const hash = crypto.createHash('sha512-256').update(signature).digest() const first4Bytes = hash.slice(0, 4)
printInfo(` SHA-512/256("${signature}"):`) printInfo(` Full hash: ${formatHex(hash)}`) printInfo(` First 4 bytes: ${formatHex(first4Bytes)}`) printInfo('') printInfo(` getSelector(): ${formatHex(demoMethod.getSelector())}`) printInfo(` Match: ${Buffer.from(first4Bytes).toString('hex') === Buffer.from(demoMethod.getSelector()).toString('hex')}`)
// Step 8: Methods with tuple arguments printStep(8, 'Methods with Tuple Arguments')
const tupleArgMethod = ABIMethod.fromSignature('processOrder((uint64,address,uint64),bool)uint64')
printInfo(`Signature: processOrder((uint64,address,uint64),bool)uint64`) printInfo(` name: ${tupleArgMethod.name}`) printInfo(` args count: ${tupleArgMethod.args.length}`) printInfo(` args[0].type: ${tupleArgMethod.args[0].type.toString()} (tuple)`) printInfo(` args[1].type: ${tupleArgMethod.args[1].type.toString()}`) printInfo(` returns: ${tupleArgMethod.returns.type.toString()}`) printInfo(` selector: ${formatHex(tupleArgMethod.getSelector())}`)
const nestedTupleMethod = ABIMethod.fromSignature('nested(((uint64,bool),string))void')
printInfo(`\nSignature: nested(((uint64,bool),string))void`) printInfo(` name: ${nestedTupleMethod.name}`) printInfo(` args[0].type: ${nestedTupleMethod.args[0].type.toString()}`) printInfo(` returns: ${nestedTupleMethod.returns.type.toString()}`) printInfo(` selector: ${formatHex(nestedTupleMethod.getSelector())}`)
// Step 9: Methods with tuple returns printStep(9, 'Methods with Tuple Returns')
const tupleReturnMethod = ABIMethod.fromSignature('getInfo(address)(uint64,string,bool)')
printInfo(`Signature: getInfo(address)(uint64,string,bool)`) printInfo(` name: ${tupleReturnMethod.name}`) printInfo(` args: [${tupleReturnMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns.type: ${tupleReturnMethod.returns.type.toString()} (tuple)`) printInfo(` selector: ${formatHex(tupleReturnMethod.getSelector())}`)
const complexReturnMethod = ABIMethod.fromSignature('swap(uint64,uint64)(uint64,uint64,address)')
printInfo(`\nSignature: swap(uint64,uint64)(uint64,uint64,address)`) printInfo(` name: ${complexReturnMethod.name}`) printInfo(` args: [${complexReturnMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${complexReturnMethod.returns.type.toString()}`) printInfo(` selector: ${formatHex(complexReturnMethod.getSelector())}`)
// Step 10: Methods with array arguments printStep(10, 'Methods with Array Arguments')
const arrayArgMethod = ABIMethod.fromSignature('batchTransfer(address[],uint64[])bool')
printInfo(`Signature: batchTransfer(address[],uint64[])bool`) printInfo(` name: ${arrayArgMethod.name}`) printInfo(` args: [${arrayArgMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${arrayArgMethod.returns.type.toString()}`) printInfo(` selector: ${formatHex(arrayArgMethod.getSelector())}`)
const staticArrayMethod = ABIMethod.fromSignature('setVotes(uint64[5])void')
printInfo(`\nSignature: setVotes(uint64[5])void`) printInfo(` name: ${staticArrayMethod.name}`) printInfo(` args: [${staticArrayMethod.args.map((a) => a.type.toString()).join(', ')}]`) printInfo(` returns: ${staticArrayMethod.returns.type.toString()}`) printInfo(` selector: ${formatHex(staticArrayMethod.getSelector())}`)
// Step 11: getSignature() method printStep(11, 'Reconstructing Signature with getSignature()')
printInfo('The getSignature() method returns the canonical signature string.') printInfo('')
const testMethods = [ ABIMethod.fromSignature('transfer(address,uint64)uint64'), ABIMethod.fromSignature('get()uint64'), ABIMethod.fromSignature('set(uint64)void'), ABIMethod.fromSignature('process((uint64,bool),string)void'), ]
for (const method of testMethods) { printInfo(` getSignature(): ${method.getSignature()}`) }
// Step 12: Selector uniqueness printStep(12, 'Selector Uniqueness')
printInfo('Each method signature produces a unique 4-byte selector.') printInfo('Different signatures = different selectors.') printInfo('')
const differentMethods = [ ABIMethod.fromSignature('get()uint64'), ABIMethod.fromSignature('get()string'), ABIMethod.fromSignature('get(uint64)uint64'), ABIMethod.fromSignature('fetch()uint64'), ]
printInfo('Method | Selector') printInfo('------------------------|----------')
for (const method of differentMethods) { const sig = method.getSignature().padEnd(22) const sel = formatHex(method.getSelector()) printInfo(`${sig} | ${sel}`) }
// Step 13: Summary printStep(13, 'Summary')
printInfo('ABIMethod provides tools for working with ABI method signatures:') printInfo('') printInfo('Parsing:') printInfo(' ABIMethod.fromSignature(sig) - Parse a method signature string') printInfo('') printInfo('Properties:') printInfo(' method.name - Method name (string)') printInfo(' method.args - Array of argument descriptors') printInfo(' method.args[i].type - ABIType of argument i') printInfo(' method.returns.type - ABIType of return value') printInfo('') printInfo('Methods:') printInfo(' getSignature() - Get canonical signature string') printInfo(' getSelector() - Get 4-byte selector (Uint8Array)') printInfo('') printInfo('Selector computation:') printInfo(' selector = SHA-512/256(signature)[0:4]') printInfo('') printInfo('Supported signatures:') printInfo(' - Simple: name(type1,type2)returnType') printInfo(' - No args: name()returnType') printInfo(' - Void: name(type1)void') printInfo(' - Tuples: name((t1,t2),t3)(r1,r2)') printInfo(' - Arrays: name(type[],type[N])type')
printSuccess('ABI Method example completed successfully!')}
main()