コントラクトと接続しよう
これまでフロントエンドにUIを用意し、ウォレットとの接続も出来ました!
このレッスンではあなたのスマートコントラクトをデプロイし、フロントエンドと連携します。
🌵 コントラクトとの接続部分を実装しましょう
引き続きpackages/client
ディレクトリのファイルを操作していきます。
📁 hooks
ディレクトリ
hooks
ディレクトリ内にuseContract.ts
というファイルを作成し、以下のコードを記述してください。
💁 現時点ではまだ用意していないファイル からimportしている箇所があるためエラーメッセージが出ても無視して大丈夫です。
import { BigNumber, ethers } from "ethers";
import { useEffect, useState } from "react";
import { USDCToken as UsdcContractType } from "../typechain-types";
import { JOEToken as JoeContractType } from "../typechain-types";
import { AMM as AmmContractType } from "../typechain-types";
import AmmArtifact from "../utils/AMM.json";
import { getEthereum } from "../utils/ethereum";
import UsdcArtifact from "../utils/USDCToken.json";
import JoeArtifact from "../utils/USDCToken.json";
export const UsdcAddress = "コントラクトのデプロイ先アドレス";
export const JoeAddress = "コントラクトのデプロイ先アドレス";
export const AmmAddress = "コントラクトのデプロイ先アドレス";
export type TokenType = {
symbol: string;
contract: UsdcContractType | JoeContractType;
};
export type AmmType = {
sharePrecision: BigNumber;
contract: AmmContractType;
};
type ReturnUseContract = {
usdc: TokenType | undefined;
joe: TokenType | undefined;
amm: AmmType | undefined;
};
export const useContract = (
currentAccount: string | undefined
): ReturnUseContract => {
const [usdc, setUsdc] = useState<TokenType>();
const [joe, setJoe] = useState<TokenType>();
const [amm, setAmm] = useState<AmmType>();
const ethereum = getEthereum();
const getContract = useCallback(
(
contractAddress: string,
abi: ethers.ContractInterface,
storeContract: (_: ethers.Contract) => void
) => {
if (!ethereum) {
console.log("Ethereum object doesn't exist!");
return;
}
if (!currentAccount) {
// ログインしていない状態でコン トラクトの関数を呼び出すと失敗するため
// currentAccountがundefinedの場合はcontractオブジェクトもundefinedにします。
console.log("currentAccount doesn't exist!");
return;
}
try {
const provider = new ethers.providers.Web3Provider(
ethereum as unknown as ethers.providers.ExternalProvider
);
const signer = provider.getSigner(); // 簡易実装のため、引数 なし = 初めのアカウント(account#0)を使用する
const Contract = new ethers.Contract(contractAddress, abi, signer);
storeContract(Contract);
} catch (error) {
console.log(error);
}
},
[ethereum, currentAccount]
);
const generateUsdc = async (contract: UsdcContractType) => {
try {
const symbol = await contract.symbol();
setUsdc({ symbol: symbol, contract: contract } as TokenType);
} catch (error) {
console.log(error);
}
};
const generateJoe = async (contract: UsdcContractType) => {
try {
const symbol = await contract.symbol();
setJoe({ symbol: symbol, contract: contract } as TokenType);
} catch (error) {
console.log(error);
}
};
const generateAmm = async (contract: AmmContractType) => {
try {
const precision = await contract.PRECISION();
setAmm({ sharePrecision: precision, contract: contract } as AmmType);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getContract(UsdcAddress, UsdcArtifact.abi, (Contract: ethers.Contract) => {
generateUsdc(Contract as UsdcContractType);
});
getContract(JoeAddress, JoeArtifact.abi, (Contract: ethers.Contract) => {
generateJoe(Contract as JoeContractType);
});
getContract(AmmAddress, AmmArtifact.abi, (Contract: ethers.Contract) => {
generateAmm(Contract as AmmContractType);
});
}, [ethereum, currentAccount, getContract]);
return {
usdc,
joe,
amm,
};
};
ファイル上部は必要な関数などのimportと、型定義をしています。 💁 現時点ではまだ用意していないファイルからimportしている箇所があるためエラーメッセージが出ても無視して大丈夫です。
export type TokenType = {
symbol: string;
contract: UsdcContractType | JoeContractType;
};
フロントエンドで使用するトークンコントラクトのオブジェクトの型になります。
USDCまたはJOEのコントラクトのインスタンスとトークンシンボルをstring型で保持します。
export type AmmType = {
sharePrecision: BigNumber;
contract: AmmContractType;
};
フロントエンドで使用するAMMコントラクトのオブジェクトの型になります。
AMMのコントラクトのインスタンスとPRECISIONを保持します。
const getContract = useCallback(
(
contractAddress: string,
abi: ethers.ContractInterface,
storeContract: (_: ethers.Contract) => void
) => {
if (!ethereum) {
console.log("Ethereum object doesn't exist!");
return;
}
if (!currentAccount) {
// ログインしていない状態でコントラクトの関数を呼び出すと失敗するため
// currentAccountがundefinedの場合はcontractオブジェクトもundefinedにします。
console.log("currentAccount doesn't exist!");
return;
}
try {
const provider = new ethers.providers.Web3Provider(
ethereum as unknown as ethers.providers.ExternalProvider
);
const signer = provider.getSigner(); // 簡易実装のため、引数なし = 初めのアカウント(account#0)を使用する
const Contract = new ethers.Contract(contractAddress, abi, signer);
storeContract(Contract);
} catch (error) {
console.log(error);
}
},
[ethereum, currentAccount]
);
getContract
は引数で指定されたアドレスとabiのコントラクトのインスタンスを取得する関数です。
インスタンスを取得できたら引数で渡された関数に渡します。
storeContract(Contract);
const generateUsdc = async (contract: UsdcContractType) => {
try {
const symbol = await contract.symbol();
setUsdc({ symbol: symbol, contract: contract } as TokenType);
} catch (error) {
console.log(error);
}
};
const generateJoe = async (contract: UsdcContractType) => {
try {
const symbol = await contract.symbol();
setJoe({ symbol: symbol, contract: contract } as TokenType);
} catch (error) {
console.log(error);
}
};
const generateAmm = async (contract: AmmContractType) => {
try {
const precision = await contract.PRECISION();
setAmm({ sharePrecision: precision, contract: contract } as AmmType);
} catch (error) {
console.log(error);
}
};
それぞれ引数で渡されたコントラクトのインスタンスから、フロントエンドで使用するオブジェクトに変換しています。
useEffect(() => {
getContract(UsdcAddress, UsdcArtifact.abi, (Contract: ethers.Contract) => {
generateUsdc(Contract as UsdcContractType);
});
getContract(JoeAddress, JoeArtifact.abi, (Contract: ethers.Contract) => {
generateJoe(Contract as JoeContractType);
});
getContract(AmmAddress, AmmArtifact.abi, (Contract: ethers.Contract) => {
generateAmm(Contract as AmmContractType);
});
}, [ethereum, currentAccount, getContract]);
各コントラクトの取得からオブジェクトの作成までを行っています。