Contract Interaction and Decentralized ApplicationsIntroductionA smart contract becomes useful when external actors can reliably communicate with it. Deployment places contract code at a blockchain address, but applications must still discover the address, encode function calls, sign transactions, submit them through a node, monitor their progress, decode returned values, and respond to emitted events. These activities form the practical foundation of decentralized application (dApp) development. A dApp usually combines four layers: - On-chain contracts, which enforce rules and update blockchain state.
- A wallet or signing system, which controls user private keys and authorizes transactions.
- An RPC interface, which allows software to read blockchain data and submit requests.
- A user interface or backend service, which presents information, prepares calls, indexes events, and coordinates workflows.
This chapter explains how these layers interact and how developers can design secure, dependable integrations. Learning ObjectivesBy the end of this chapter, a learner should be able to: - explain the relationship between contract addresses, ABIs, wallets, RPC providers, and libraries;
- distinguish read-only calls from state-changing transactions;
- construct, sign, submit, and monitor contract transactions;
- interpret transaction receipts, confirmations, failures, and events;
- integrate contracts into browser applications and backend services;
- identify common security, privacy, reliability, and governance risks; and
- apply professional practices to a small dApp integration.
1. The Contract Interaction ModelA deployed contract is identified by an address on a particular blockchain network. An address alone is insufficient for meaningful interaction: software also needs to know the contract's callable functions, input types, output types, events, and custom errors. This information is represented by the Application Binary Interface (ABI). For example, an ABI fragment may describe: {
"inputs": [{"name": "amount", "type": "uint256"}],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}The ABI tells a library how to encode a call to deposit(uint256) and how to interpret the result. It does not contain the contract's source code or prove that a deployed address contains the expected implementation. Address, network, ABI, and deployment version must therefore be managed together. A typical interaction follows this sequence: - The application selects a network and RPC endpoint.
- It creates a provider for reading blockchain data.
- It connects a contract address with its ABI.
- For a read, it performs an
eth_call without changing state. - For a write, a wallet signs a transaction locally or through a wallet interface.
- The signed transaction is broadcast through an RPC node.
- The application tracks the transaction hash and receipt.
- Confirmed events and state changes update the interface or backend records.
The blockchain is authoritative for state. A user interface is only a view of that state and must not treat a locally displayed result as final until the network has accepted the transaction at the required confirmation level. 2. Addresses, Networks, and Deployment MetadataAn address is meaningful only in the context of a chain and network. The same hexadecimal address may refer to different contracts on different networks, while a contract may be deployed at different addresses on mainnet, a testnet, and a local development chain. Professional projects maintain deployment metadata such as: | Field | Purpose |
|---|
| Network and chain identifier | Prevents interaction with the wrong chain | | Contract address | Locates the deployed instance | | ABI version | Describes the callable interface | | Deployment block | Supports event indexing from a known point | | Implementation address | Important for upgradeable contracts | | Source verification reference | Helps users and auditors inspect code | | Deployment transaction hash | Provides an auditable deployment record |
Applications should validate the connected chain before sending transactions. A network mismatch can cause failed calls, misleading data, or funds being sent to an unintended deployment. Configuration should be separated by environment, and production addresses should not be accepted from arbitrary user input. Contract identity and upgradeabilityFor a non-upgradeable contract, code at an address normally remains fixed after deployment. Upgradeable designs commonly use a proxy address that delegates calls to an implementation address. Users interact with the proxy, but the ABI and implementation may change according to an upgrade mechanism. This creates additional requirements: - monitor implementation changes;
- verify upgrade authorization;
- maintain compatible storage layouts;
- publish updated ABIs; and
- communicate upgrade risk to users.
An address is therefore not always a complete description of contract behavior. 3. ABI Encoding and DecodingAn ABI maps human-readable declarations to binary data. A function call contains a four-byte function selector followed by ABI-encoded arguments. The selector is derived from the first four bytes of the hash of the canonical function signature, such as transfer(address,uint256). Libraries normally calculate this automatically. Arguments must use the exact declared types. For example, a value representing token units should be encoded as an integer in the token's smallest unit, not as a decimal string that the contract does not understand. If a token has 18 decimal places, 1 token is represented as 1 × 10¹⁸ base units. Applications should use arbitrary-precision integer types rather than JavaScript floating-point numbers for token amounts. ABIs also describe: - return values from view and pure functions;
- indexed and non-indexed event fields;
- custom errors used in transaction reverts; and
- payable functions that accept native currency.
A mismatch between ABI and deployed bytecode may cause an encoding error, a revert, incorrectly decoded data, or—especially with loosely checked interfaces—an interaction with unintended behavior. ABI files should be generated from the build process and version-controlled. 4. Providers, RPC, and WalletsAn RPC provider exposes blockchain node methods through a remote procedure call interface. Common JSON-RPC methods include eth_chainId, eth_call, eth_getBalance, eth_getCode, eth_sendRawTransaction, and eth_getTransactionReceipt. Libraries such as ethers.js and web3.py provide higher-level abstractions over these methods. A provider generally performs read operations. A signer represents an account capable of authorizing transactions. In a browser, the signer is commonly supplied by a wallet such as MetaMask through an injected provider. In a backend, a signer may use a protected key-management service, hardware security module, or restricted wallet process. The separation is important: - a provider can read without possessing private keys;
- a signer can authorize writes but should not expose its private key to application code unnecessarily; and
- a contract object may be connected to either a provider or a signer.
Private keys must never be placed in frontend source code, public repositories, client-side storage, or ordinary server logs. Backend signing keys should be isolated, access-controlled, monitored, and limited in value and authority. Browser interaction exampleThe following illustrative ethers.js pattern shows the conceptual separation between a provider, signer, and contract. Exact APIs vary by library version. const browserProvider = new ethers.BrowserProvider(window.ethereum);
await browserProvider.send("eth_requestAccounts", []);
const network = await browserProvider.getNetwork();
if (network.chainId !== EXPECTED_CHAIN_ID) {
throw new Error("Unsupported network");
}
const signer = await browserProvider.getSigner();
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
const currentValue = await contract.value(); // read
const tx = await contract.setValue(42); // signed write
const receipt = await tx.wait(); // confirmationThe example is not a complete production application. It should additionally handle rejected wallet requests, unavailable providers, wrong accounts, network switching, transaction replacement, and contract errors. 5. Reads and WritesA read-only call executes contract code against a selected blockchain state without creating a mined transaction. It normally does not require a signature or gas payment from the caller. However, it can fail if the function reverts, if the RPC node is unavailable, or if the supplied parameters are invalid. A state-changing transaction must be signed and included in a block. It may change contract storage, transfer assets, emit events, and consume gas. The transaction sender pays the execution cost, subject to the network's fee model. A useful conceptual gas-cost relationship is: Transaction fee = gas used × effective gas price Here, gas used is the computational resource consumed, measured in gas units, and effective gas price is the amount of native currency paid per gas unit. The relationship applies to ordinary fee accounting, although exact fee rules differ among networks, including networks with base-fee and priority-fee components. The product has units of native currency. A read can be simulated before a write. Simulation may reveal an expected revert, but it cannot guarantee success: state may change between simulation and mining, prices may change, permissions may be altered, or the transaction may use different gas parameters. Transaction fieldsCommon transaction fields include: to: destination contract address;data: ABI-encoded function call and arguments;value: native currency sent with the call;nonce: sender's transaction sequence number;- gas-limit information; and
- fee parameters appropriate to the network.
The nonce prevents ambiguous ordering for transactions from one account. Concurrent backend submissions must coordinate nonce management carefully; otherwise transactions may become stuck, replaced, or rejected. 6. Transaction Lifecycle and Failure HandlingA transaction normally moves through several states: - Prepared: parameters and estimated fees have been assembled.
- Awaiting signature: the wallet requests user authorization.
- Signed: the transaction is cryptographically authorized.
- Broadcast: a node accepts the raw transaction and returns a hash.
- Pending: the transaction is waiting for inclusion.
- Included: a block contains the transaction.
- Confirmed: additional blocks or protocol-specific finality provide greater confidence.
- Finalized or settled: the network's finality model considers reversal highly unlikely or impossible under normal assumptions.
- Reverted: the transaction was included but contract execution failed; consumed resources are generally not recovered.
A transaction hash is an identifier, not proof of success. Applications must obtain a receipt and inspect its status. They should distinguish at least submitted, confirmed, failed, replaced, and unknown states. An RPC timeout does not prove failure: the transaction may have been accepted but the response lost. For valuable operations, interfaces should avoid claiming completion before an appropriate confirmation state. Backend systems should reconcile local records with chain data and support retries that are safe under repeated execution. Idempotency and retriesA retry can submit the same logical action twice. If the contract operation is not naturally idempotent, the result may be duplicated. Applications can reduce this risk by using: - unique request identifiers checked by the contract;
- client-side operation records linked to transaction hashes;
- nonce-aware submission queues; and
- reconciliation based on events and final state rather than HTTP responses alone.
7. Events and LogsEvents are records emitted during contract execution. They are stored in transaction logs and can be filtered by contract address, event signature, indexed parameters, and block range. Events are valuable for user interfaces, analytics, notifications, and backend indexing. For example, a token transfer event may include indexed from and to addresses and a non-indexed amount. Indexed fields support efficient filtering, but event data is not a replacement for contract storage. A contract may emit an event incorrectly, omit an event, or emit several events during one transaction. Applications requiring authoritative state should verify relevant storage or reconstruct state according to the contract's documented rules. A backend indexer should record: - chain identifier;
- block number and block hash;
- transaction hash and transaction index;
- log index;
- contract address;
- decoded event name and arguments; and
- processing status.
The combination of transaction hash and log index commonly identifies a log within a chain history. Indexers must also handle chain reorganizations. Data from a removed block should be marked or rolled back, and affected blocks should be reprocessed according to the network's confirmation policy. A frontend can subscribe to new events through WebSocket or polling, but subscriptions can disconnect and may not provide historical completeness. A robust application combines live updates with periodic backfilling. 8. Integrating a Contract with a Web ApplicationA web dApp should treat the wallet as an external dependency. A practical interaction flow is: - Explain why wallet access is needed.
- Request accounts only after a user action.
- Check chain and account state.
- Display the intended contract, function, parameters, and approximate fee.
- Request signature.
- Show the transaction hash and pending status.
- Wait for the required confirmation.
- Refresh state from the blockchain or an indexed data source.
- Present success or a useful failure explanation.
Frontends should not trust user-supplied token symbols, decimals, or addresses without validation. Numeric inputs require careful conversion and bounds checking. For example, an amount must be parsed according to the token's declared decimals and checked for positivity, maximum limits, and available balance. User experience and security are linked. A clear confirmation screen helps users detect a malicious or misleading transaction. Contracts should use human-readable errors where possible, and the interface should explain whether a failure occurred during signature, submission, execution, or confirmation. 9. Backend Services and IndexersBackend services are useful for event indexing, scheduled actions, analytics, access-controlled administration, and integration with conventional databases. A backend may use a read-only provider for public data and a controlled signer for authorized operations. A sound architecture separates responsibilities: - the contract remains the source of truth for ownership and settlement;
- the indexer stores query-friendly projections;
- the application database stores off-chain metadata and workflow state; and
- reconciliation jobs compare local projections with chain data.
Off-chain records should not silently override on-chain state. If a database says that a withdrawal succeeded but no corresponding finalized transaction exists, the system should classify the record as unverified rather than presenting it as settled. RPC providers differ in rate limits, archival access, indexing features, and reliability. Production systems commonly use health checks, bounded retries with backoff, provider redundancy, request caching where safe, and monitoring for lag between the chain head and the indexed head. 10. Applied Case: Illustrative Ticketing dAppBackgroundConsider an illustrative decentralized event-ticketing application. A contract sells tickets, records ownership, and emits TicketPurchased and TicketTransferred events. The web application allows users to purchase and display tickets, while a backend indexes events for search and venue verification. ProblemThe initial implementation displays “ticket purchased” immediately after receiving a transaction hash. Users occasionally see a ticket in the interface even when the transaction later reverts. The backend also misses events after RPC disconnections. AnalysisThe design confuses broadcast with confirmation and treats a live subscription as a complete event history. It also stores only the latest transaction hash, making reconciliation difficult. Ticket ownership must be derived from confirmed contract state and events, not from the browser's optimistic message. ApproachThe revised system uses the following process: - The frontend checks the chain identifier, ticket price, sale status, and user balance.
- It simulates the purchase and displays the expected native-currency value and fee estimate.
- The wallet signs the transaction.
- The interface records the transaction hash with a
pending status. - The backend observes the transaction and waits for the application's confirmation threshold.
- After confirmation, it decodes
TicketPurchased, stores block and log identifiers, and queries ownership from the contract. - If the transaction receipt indicates failure, the interface marks the operation
failed and does not issue a ticket. - The indexer periodically backfills blocks after its last finalized checkpoint and handles removed logs during reorganizations.
OutcomeThe application no longer treats a submitted transaction as completed. Users receive clear pending and failure states, while the backend can recover from temporary disconnections. The indexed database becomes a queryable projection, but contract state remains authoritative. Learning pointsThe case demonstrates four general principles: transaction hashes are not receipts, events require durable indexing, optimistic interfaces need reconciliation, and confirmation policy should reflect the value and risk of the operation. 11. Security, Governance, and Regulatory ConsiderationsContract integration creates risks beyond Solidity vulnerabilities. Common integration risks include wrong-network transactions, phishing interfaces, malicious RPC endpoints, ABI/address substitution, leaked signing keys, unlimited token approvals, replay or duplicate submissions, and misleading event displays. Best practices include: - pinning chain identifiers and verified contract addresses;
- obtaining ABIs from trusted build artifacts;
- displaying destination and value before signature;
- using least-privilege operational accounts;
- separating testnet, staging, and production credentials;
- limiting token approvals where practical;
- validating decoded data and handling malformed responses;
- monitoring contract upgrades and administrative actions; and
- recording an auditable transaction and event trail.
Privacy also requires attention. Public blockchains expose addresses, timestamps, values, and interaction patterns. Connecting an address to a person's identity may create sensitive personal data. Applications should avoid unnecessary collection, protect off-chain identity records, and explain what blockchain publication means. Regulatory treatment varies by jurisdiction and use case. Token sales, custody, payments, identity, financial services, consumer protection, sanctions compliance, and data protection may all be relevant. Technical immutability does not remove legal obligations, and a dApp's decentralized interface may still be operated by identifiable organizations. 12. Contemporary Developments and Professional PracticeModern dApps increasingly use account abstraction, smart-contract wallets, relayers, batched calls, and gas sponsorship. These features can improve usability but add trust assumptions and more complex authorization flows. Developers must identify who pays fees, who can submit a user operation, how replay protection works, and what happens if a relayer is unavailable. Layer-2 networks can reduce transaction costs and increase throughput, but bridges, withdrawal periods, sequencer behavior, and differing finality assumptions affect application design. A dApp should state clearly which network is active and should not assume that confirmation semantics are identical across chains. AI tools can assist with ABI documentation, test generation, log classification, and anomaly detection. They must not be trusted as unsupervised authorities for addresses, transaction parameters, security conclusions, or compliance decisions. Generated code and explanations require human review, testing, and preferably independent security assessment. A professional delivery process includes unit tests, integration tests against a local chain, testnet trials, wallet and network-switch tests, failure injection, event-reorganization tests, monitoring, incident procedures, and documented deployment artifacts. Chapter SummaryContract interaction connects deployed blockchain code with wallets, RPC providers, libraries, web interfaces, and backend services. An ABI describes how functions, outputs, events, and errors are encoded, while an address identifies a deployment only within a particular network. Read-only calls query state without a mined transaction; state-changing calls require signing, fee payment, inclusion, and confirmation. Reliable applications track the complete transaction lifecycle and distinguish a transaction hash from a successful receipt. Events support indexing and responsive interfaces but must be handled with durable storage, historical backfilling, and reorganization awareness. Frontends should provide transparent signing flows, validate networks and parameters, and refresh from authoritative chain state. Backend systems should use controlled keys, reconciliation, provider resilience, and clear separation between on-chain truth and off-chain projections. Security, privacy, upgrade governance, regulatory context, and evolving technologies such as account abstraction and layer-2 networks are essential parts of professional dApp design. Key TermsABI: A formal interface describing contract functions, arguments, returns, events, and errors. Address: A network-specific identifier for an account or deployed contract. Provider: Software that communicates with a blockchain node, generally for reading data and broadcasting requests. Signer: An account interface capable of authorizing transactions. RPC: Remote procedure call communication between an application and a blockchain node. Read-only call: Contract execution that does not create a state-changing transaction. Transaction: A signed request to execute an operation on a blockchain. Nonce: A sequential transaction number associated with an account. Gas: A unit measuring computational execution resources. Receipt: The post-inclusion record containing transaction status, logs, and block information. Confirmation: Additional chain acceptance after a transaction is included in a block. Finality: The condition under which reversal is highly unlikely or protocolically prevented. Event: A contract-emitted log used to communicate execution information. Indexer: A service that transforms blockchain data into searchable application records. Reorganization: A change in the canonical chain that may remove previously observed blocks or logs. Signer key: Secret cryptographic material authorizing transactions. Proxy: A contract that delegates execution to another implementation contract. dApp: An application whose critical rules or settlement depend on decentralized infrastructure. Idempotency: The property that repeating a logical request does not produce an unintended duplicate effect. Review Questions5 Short Answer Questions- Why are both an ABI and a contract address required for most programmatic interactions?
- What is the difference between a provider and a signer?
- Why does a transaction hash not prove that a contract operation succeeded?
- What information can an event log provide to a backend indexer?
- State two risks created by upgradeable contracts.
5 Long Answer Questions- Explain the complete lifecycle of a state-changing transaction from preparation to finality.
- Compare browser-based wallet integration with backend signing, including their security implications.
- Describe how a robust event-indexing service should handle disconnections and chain reorganizations.
- Explain why off-chain databases should be treated as projections rather than replacements for on-chain state.
- Discuss how network selection, ABI versioning, and deployment metadata contribute to application reliability.
3 Analytical / Application Questions- A user interface marks a purchase complete as soon as it receives a transaction hash. Design a corrected workflow and justify each state transition.
- A backend submits two transactions concurrently from one account and one remains pending. Analyze the likely nonce-related causes and propose operational controls.
- Design a security and privacy checklist for a dApp that handles user identities, token approvals, event indexing, and upgradeable contracts.
Multiple Choice Questions with AnswersQuestion 1Which combination is normally required to interpret and invoke a deployed contract function? A. Only the contract address B. The contract address, network context, and compatible ABI C. Only the wallet seed phrase D. A block number without an ABI Correct Answer: B. The contract address, network context, and compatible ABI Question 2What is the primary role of a provider in a dApp? A. To expose private keys to the frontend B. To replace contract bytecode C. To communicate with blockchain nodes for reads and requests D. To guarantee transaction finality Correct Answer: C. To communicate with blockchain nodes for reads and requests Question 3Which operation normally requires a signed transaction? A. Reading a public balance B. Calling a pure function C. Querying an event log D. Updating contract storage Correct Answer: D. Updating contract storage Question 4What does a transaction receipt allow an application to determine? A. Whether the included transaction succeeded and what logs it produced B. The user's private key C. The contract's source-code license automatically D. That an RPC timeout means failure Correct Answer: A. Whether the included transaction succeeded and what logs it produced Question 5A transaction fee is conceptually calculated as: A. gas used ÷ effective gas price B. effective gas price ÷ gas used C. gas used × effective gas price D. block number × nonce Correct Answer: C. gas used × effective gas price Question 6Why should a backend indexer store block and log identifiers with decoded events? A. To reconstruct and reconcile event records reliably B. To create a new private key for each event C. To prevent all contract upgrades D. To eliminate the need for a contract address Correct Answer: A. To reconstruct and reconcile event records reliably Question 7Which statement about a transaction hash is correct? A. It proves that execution succeeded B. It identifies the transaction but must be combined with receipt status to assess success C. It is the same as a user's private key D. It guarantees immediate finality on every network Correct Answer: B. It identifies the transaction but must be combined with receipt status to assess success Question 8What is a major danger of storing a backend signing key in frontend JavaScript? A. The ABI becomes shorter B. Public users may obtain the key and authorize transactions C. Read-only calls stop working D. Events can no longer be indexed Correct Answer: B. Public users may obtain the key and authorize transactions Question 9Why should an application check the connected chain identifier before sending a transaction? A. To make the ABI unnecessary B. To ensure the user interacts with the intended network deployment C. To increase the contract's storage capacity D. To remove the need for confirmation Correct Answer: B. To ensure the user interacts with the intended network deployment Question 10What is a suitable response to an RPC timeout after transaction submission? A. Assume failure and submit unlimited duplicates immediately B. Delete all local records C. Check the transaction and receipt because the transaction may already have been accepted D. Reveal the signing key to the RPC provider Correct Answer: C. Check the transaction and receipt because the transaction may already have been accepted
|