Contract Interactions
HumanWallet provides functions for interacting with smart contracts, including reading contract state and executing transactions. All write operations are gasless through account abstraction.
Functions Overview
writeContract()- Execute single contract transactionswriteContracts()- Execute multiple transactions in batchreadContract()- Read contract statesignTypedData()- Sign structured data
writeContract()
Executes a single contract function call as a gasless transaction.
Function Signature
function writeContract(config: Config, args: WriteContractParameters): Promise<string>Parameters
config
- Type:
Config - Description: Configuration object from
createConfig()with authenticated client
args
- Type:
WriteContractParameters - Properties:
address: Contract address (0x${string})abi: Contract ABI arrayfunctionName: Function name to callargs: Array of function arguments (optional)value: ETH value to send (optional, defaults to0n)
Usage Example
import { writeContract } from "@humanwallet/core"
// First authenticate user
const { sessionKeyAccount, kernelClient } = await login("alice", config)
// Update config with authenticated client
const authenticatedConfig = {
...config,
kernelClient,
sessionKeyAccount,
}
// Execute a token transfer
const hash = await writeContract(authenticatedConfig, {
address: "0x...", // Token contract address
abi: [
{
name: "transfer",
type: "function",
inputs: [
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" },
],
outputs: [{ name: "", type: "bool" }],
stateMutability: "nonpayable",
},
],
functionName: "transfer",
args: ["0x742d35Cc6634C0532925a3b8D52ECC5c5e9fA008", BigInt("1000000000000000000")], // 1 token
})
console.log("Transaction hash:", hash)Advanced Examples
NFT Minting
const mintHash = await writeContract(authenticatedConfig, {
address: "0x...", // NFT contract
abi: nftAbi,
functionName: "mint",
args: [sessionKeyAccount.address, 1], // mint to self, token ID 1
})Contract with ETH Value
const donationHash = await writeContract(authenticatedConfig, {
address: "0x...", // Donation contract
abi: donationAbi,
functionName: "donate",
value: BigInt("1000000000000000000"), // 1 ETH
})writeContracts()
Executes multiple contract function calls in a single batch transaction.
Function Signature
function writeContracts(config: Config, calls: WriteContractParameters[]): Promise<string>Parameters
config: Configuration object with authenticated clientcalls: Array of contract call parameters
Usage Example
// Batch multiple operations
const batchHash = await writeContracts(authenticatedConfig, [
{
address: "0x...", // Token contract
abi: tokenAbi,
functionName: "approve",
args: ["0x...", BigInt("1000000000000000000")],
},
{
address: "0x...", // DEX contract
abi: dexAbi,
functionName: "swap",
args: [BigInt("1000000000000000000"), BigInt("950000000000000000")],
},
])
console.log("Batch transaction hash:", batchHash)Benefits of Batching
- Atomic Operations: All transactions succeed or fail together
- Gas Efficiency: Single user operation instead of multiple
- Better UX: One signature for multiple actions
- Reduced Latency: Faster than sequential transactions
readContract()
Reads data from a smart contract without executing a transaction.
Function Signature
function readContract(config: Config, args: ReadContractParameters): Promise<any>Parameters
args
- Type:
ReadContractParameters - Properties:
address: Contract addressabi: Contract ABI arrayfunctionName: Function name to callargs: Array of function arguments (optional)
Usage Examples
Token Balance
const balance = await readContract(config, {
address: "0x...", // Token contract
abi: [
{
name: "balanceOf",
type: "function",
inputs: [{ name: "account", type: "address" }],
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
},
],
functionName: "balanceOf",
args: [sessionKeyAccount.address],
})
console.log("Token balance:", balance.toString())Contract State
const totalSupply = await readContract(config, {
address: "0x...",
abi: tokenAbi,
functionName: "totalSupply",
})
const name = await readContract(config, {
address: "0x...",
abi: tokenAbi,
functionName: "name",
})
console.log(`${name} total supply:`, totalSupply.toString())Complex Data Structures
// Reading a struct
const userInfo = await readContract(config, {
address: "0x...",
abi: stakingAbi,
functionName: "getUserInfo",
args: [sessionKeyAccount.address],
})
console.log("User info:", {
stakedAmount: userInfo[0].toString(),
rewardDebt: userInfo[1].toString(),
lastStakeTime: new Date(Number(userInfo[2]) * 1000),
})signTypedData()
Signs structured data according to EIP-712 standard.
Function Signature
function signTypedData(config: Config, args: SignTypedDataParameters): Promise<Signature>Parameters
args
- Type:
SignTypedDataParameters - Properties:
domain: EIP-712 domain separatortypes: Type definitions for the dataprimaryType: The primary type being signedmessage: The actual data to sign
Usage Example
// Sign a permit message for gasless token approval
const signature = await signTypedData(authenticatedConfig, {
domain: {
name: "MyToken",
version: "1",
chainId: 11155111,
verifyingContract: "0x...",
},
types: {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
},
primaryType: "Permit",
message: {
owner: sessionKeyAccount.address,
spender: "0x...",
value: BigInt("1000000000000000000"),
nonce: BigInt(0),
deadline: BigInt(Math.floor(Date.now() / 1000) + 3600), // 1 hour
},
})
console.log("Signature:", signature)Signature Object
The function returns a Signature object with:
interface Signature {
r: string // First 32 bytes of signature
s: string // Second 32 bytes of signature
v: number // Recovery parameter
}Error Handling
Authentication Errors
try {
await writeContract(config, contractParams)
} catch (error) {
if (error.message.includes("Kernel client not initialized")) {
console.log("User not authenticated - please login first")
// Redirect to authentication flow
}
}Contract Execution Errors
try {
await writeContract(authenticatedConfig, contractParams)
} catch (error) {
if (error.message.includes("execution reverted")) {
console.log("Contract execution failed:", error.message)
// Handle specific contract errors
} else if (error.message.includes("insufficient funds")) {
console.log("Insufficient balance for transaction")
} else if (error.message.includes("user rejected")) {
console.log("User cancelled transaction")
}
}Read Contract Errors
try {
const result = await readContract(config, contractParams)
} catch (error) {
if (error.message.includes("call reverted")) {
console.log("Contract call reverted - check parameters")
} else if (error.message.includes("network")) {
console.log("Network error - please try again")
}
}Best Practices
Gas Optimization
// Batch related operations
const operations = [approveTokenOperation, swapTokenOperation, stakeLPTokenOperation]
// Execute as single batch instead of 3 separate transactions
await writeContracts(authenticatedConfig, operations)Error Recovery
async function executeWithRetry(operation: () => Promise<string>, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation()
} catch (error) {
if (i === maxRetries - 1) throw error
// Wait before retry
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)))
}
}
}
const hash = await executeWithRetry(() => writeContract(authenticatedConfig, contractParams))Type Safety
// Define contract types for better TypeScript support
interface TokenContract {
address: `0x${string}`
abi: typeof tokenAbi
}
async function transferToken(contract: TokenContract, to: `0x${string}`, amount: bigint) {
return writeContract(authenticatedConfig, {
address: contract.address,
abi: contract.abi,
functionName: "transfer",
args: [to, amount],
})
}Integration Examples
DeFi Application
class DeFiService {
constructor(private config: Config) {}
async swapTokens(tokenA: string, tokenB: string, amount: bigint) {
// 1. Check allowance
const allowance = await readContract(this.config, {
address: tokenA,
abi: erc20Abi,
functionName: "allowance",
args: [this.config.sessionKeyAccount.address, DEX_CONTRACT],
})
const operations = []
// 2. Approve if needed
if (allowance < amount) {
operations.push({
address: tokenA,
abi: erc20Abi,
functionName: "approve",
args: [DEX_CONTRACT, amount],
})
}
// 3. Execute swap
operations.push({
address: DEX_CONTRACT,
abi: dexAbi,
functionName: "swapExactTokensForTokens",
args: [amount, 0n, [tokenA, tokenB], this.config.sessionKeyAccount.address],
})
// 4. Execute as batch
return writeContracts(this.config, operations)
}
}