エラーの原因
ethers.jsを使用して書き込みメソッドを呼び出すと、常に「TypeError: contract.populateTransaction.xxx is not a Function'」というエラーが発生します。
ABIにはメソッドが存在することが確認されていますが、問題は書き込みメソッドではなく、同じ名前のメソッドでもパラメーターの数が異なるオーバーロードされたメソッドにありました。
解決方法
同じ名前のメソッド(オーバーロードされたメソッド)の中から必要なものだけをContractコンストラクタに渡すためにABIをinput.lengthでフィルタリングします。その後、対応する件数名にアクセスすることで、問題を解決できます。
例
solidity例
function issueTokens(uint256 quantity) public {
_issueTokens(_msgSender(), quantity);
}
function issueTokens(address recipient, uint256 quantity) public {
_issueTokens(recipient, quantity);
}
abi例
(src/abis/myAbi.json)
[
{
"inputs": [
{
"type": "uint256",
"name": "quantity"
}
],
"name": "issueTokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"type": "address",
"name": "recipient"
},
{
"type": "uint256",
"name": "quantity"
}
],
"name": "issueTokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
TypeScript例
/* import session */
import { ethers } from 'ethers'
import { getEthSignerFromWindow } from 'lib/ethereum'
import myAbi from '../../abis/myAbi.json'
/* ...other methods... */
// issueTokens() 関数を呼び出し、1つのパラメーターを使用する
async function handleIssueTokens1(quantity: number) {
try {
// signerを取得
const signer = await getEthSignerFromWindow()
if (!signer) {
console.log('Signer is undefined.')
return
}
const contractAddress = '0x.....'
// パラメーター一つのissueTokens()をABIからフィルタリング
const filteredAbi = myAbi.filter((item) => {
return item.name === 'issueTokens' && item.inputs.length === 1
})
// コントラクトの作成
const contract = new ethers.Contract(contractAddress, filteredAbi, signer)
// 自分のウォレットアドレス
const walletAddress = await signer.getAddress()
// トランザクションデータの準備
const txData = await contract.populateTransaction.issueTokens(quantity)
// トランザクションの推定ガスを取得
const estimatedGas = await contract.estimateGas.issueTokens(quantity)
// ガスリミットを指定してトランザクションオブジェクトを作成
const transaction = {
// TOにコントラクトアドレスを設定
to: contractAddress ,
data: txData.data,
gasLimit: estimatedGas,
from: walletAddress,
value: ethers.utils.parseEther('0'),
}
// トランザクションに署名して送信
const signedTx = await signer.sendTransaction(transaction)
const receipt = await signedTx.wait()
// トランザクションが成功したか確認
if (receipt.status === 1) {
console.log('Transaction is successful.')
} else {
throw new Error(receipt.logs[0].data)
}
} catch (error) {
console.error('Error fetching data:', error)
}
}
// issueTokens() 関数を呼び出し、2つのパラメーターを使用する
async function handleIssueTokens2(recipient: string, quantity: number) {
try {
// signerを取得
const signer = await getEthSignerFromWindow()
if (!signer) {
console.log('Signer is undefined.')
return
}
const contractAddress = '0x.....'
// パラメーター二つのissueTokens()をABIからフィルタリング
const filteredAbi = myAbi.filter((item) => {
return item.name === 'issueTokens' && item.inputs.length === 2
})
// コントラクトの作成
const contract = new ethers.Contract(contractAddress, filteredAbi, signer)
// 自分のウォレットアドレス
const walletAddress = await signer.getAddress()
// トランザクションデータの準備
const txData = await contract.populateTransaction.issueTokens(recipient, quantity)
// トランザクションの推定ガスを取得
const estimatedGas = await contract.estimateGas.issueTokens(recipient, quantity)
// ガスリミットを指定してトランザクションオブジェクトを作成
const transaction = {
// TOにコントラクトアドレスを設定
to: contractAddress ,
data: txData.data,
gasLimit: estimatedGas,
from: walletAddress,
value: ethers.utils.parseEther('0'),
}
// トランザクションに署名して送信
const signedTx = await signer.sendTransaction(transaction)
const receipt = await signedTx.wait()
// トランザクションが成功したか確認
if (receipt.status === 1) {
console.log('Transaction is successful.')
} else {
throw new Error(receipt.logs[0].data)
}
} catch (error) {
console.error('Error fetching data:', error)
}
}
/* ...other methods... */
総括すると、ethers.jsのバージョン5では、「TypeError: contract.populateTransaction.xxx is not a Function」というエラーが発生することがあります。
このエラーは、書き込みメソッドを呼び出す際に、同じ名前でもパラメーターの数が異なるオーバーロードされたメソッドが存在する場合に生じます。
ABIをフィルタリングし、パラメーターの数に基づいて必要なメソッドを選択し、それに応じてアクセスすることで、このエラーを効果的に解決できます。
このアプローチにより、コントラクトとの円滑なやり取りが実現され、Ethereumアプリケーションにおけるトランザクションの成功が可能となります。