Hướng Dẫn Cần Thiết về Trừu Tượng Tài Khoản trên IoTeX: Hướng Dẫn Thực Hành về Chữ Ký p256
Với việc cộng đồng của chúng tôi bỏ phiếu hoàn toàn ủng hộ Đề Xuất Cải Tiến IoTeX 14, việc Trừu Tượng Tài Khoản cuối cùng đã được triển khai trên Mạng Chính IoTeX và Testnet, và các tính năng của nó hiện đã có sẵn cho tất cả các nhà phát triển trong hệ sinh thái. Vậy AA là gì, nó hoạt động như thế nào, và bạn có thể sử dụng nó trong ứng dụng tiếp theo của mình như thế nào?
Một Ôn Tập Nhanh
Trừu Tượng Tài Khoản (AA) như được định nghĩa bởi ERC-4337, "cho phép người dùng sử dụng ví hợp đồng thông minh chứa logic xác minh tùy ý thay vì EOAs làm tài khoản chính của họ." ERC-4337 giới thiệu nhiều lợi ích về trải nghiệm người dùng, đặc biệt là cho phép mọi người sử dụng Hợp Đồng Thông Minh làm tài khoản chính của họ.
ERC-4337 chạy trên blockchain và không yêu cầu bất kỳ thay đổi nào cho blockchain đó. Hiện tại, mã Trừu Tượng Tài Khoản của IoTeX dựa trên phiên bản phát hành ERC-4337 0.6.0.
Các Thành Phần của Cơ Sở Hạ Tầng AA
Các thành phần của cơ sở hạ tầng AA là:
- Dịch vụ Bundler: một điểm cuối cho Mạng Chính (https://bundler.w3bstream.com) và một điểm cho Testnet (https://bundler.testnet.w3bstream.com). Một Bundler là một nút ngoài chuỗi tổng hợp nhiều thao tác người dùng trừu tượng thành một giao dịch duy nhất mà blockchain cơ sở có thể xử lý. Giao dịch này được gửi đến thành phần cố định khác, gọi là Hợp Đồng
EntryPoint. EntryPointHợp Đồng: Có hai hợp đồngEntryPointđược triển khai trên IoTeX, một cho Mạng Chính (0xc3527348De07d591c9d567ce1998eFA2031B8675) và một cho Testnet (0xc3527348De07d591c9d567ce1998eFA2031B8675). Một hợp đồngEntryPointchịu trách nhiệm tạo / triển khai một số hợp đồng đặc biệt, gọi là hợp đồngAccountFactory, mà lần lượt chịu trách nhiệm tạo một số tài khoản nhất định (hợp đồng ví) có thể được sử dụng cho các mục đích cụ thể.
Để sử dụng trừu tượng tài khoản để tạo một tài khoản tùy chỉnh mới, có một số thành phần mà nhà phát triển dApp sẽ phải tạo dựa trên nhu cầu của ứng dụng của họ:
Accounthợp đồng, thực hiện logic xác thực trong phương thứcvalidateUserOp, và bất kỳ logic thực thi nào mà một thao tác người dùng có thể yêu cầu.AccountFactoryhợp đồng, chịu trách nhiệm, như đã nói ở trên, cho việc tạo / triển khai các hợp đồng tài khoản tùy chỉnh mới.- Một số mã khách hàng xây dựng các thao tác người dùng tương thích với các quy tắc xác minh được thực hiện trong
AccountFactory. - Một paymaster là một phần tùy chọn của kiến trúc AA. IoTeX cung cấp dịch vụ paymaster cho Testnet chỉ tại https://paymaster.testnet.w3bstream.com. Vai trò của paymaster là tài trợ cho gas cần thiết để thực hiện các thao tác người dùng, hoặc tài trợ hoàn toàn cho chúng hoặc cho phép người dùng thanh toán cho chúng bằng nhiều mã thông báo khác nhau.
Ví Dụ: Hợp Đồng P256AccountFactory
Là một ví dụ đầu tiên, chúng tôi đã cung cấp một hợp đồng P256AccountFactory chính thức (Mạng Chính 0xD98d2B6cBca981c777037c5784721d8179D7030b và Testnet 0x508Db1A73FcBA98594679aD4f5d8D0B880BbdaFB) cho phép các nhà phát triển tạo hợp đồng tài khoản có thể xác minh các thao tác người dùng được ký bằng mật mã "p256", thay vì bằng "secp256k1" của Ethereum và IoTeX. Điều này rất hữu ích, vì nó cho phép các nhà phát triển tạo ra các ứng dụng nơi người dùng có thể, ví dụ, ký các giao dịch bằng sinh trắc học của họ, hoặc từ bỏ các cụm từ hạt giống, hoặc thậm chí có độ bảo mật tốt hơn khi thiết bị của họ hỗ trợ một chip bảo mật dành riêng (ví dụ: Phần tử Bảo mật của Android và Enclave Bảo mật của Apple, v.v.). Mã nguồn của P256AccountFactory có thể được tìm thấy tại https://github.com/iotexproject/account-abstraction-contracts/blob/main/contracts/accounts/secp256r1/P256AccountFactory.sol trong khi hợp đồng Trừu Tượng Tài Khoản mã nguồn mở dựa trên việc triển khai của tác giả gốc của EIP-4337 cho Ethereum ở đây https://github.com/iotexproject/account-abstraction-contracts/tree/main.
Hợp đồng P256AccountFactory cũng hỗ trợ việc quản lý dịch vụ paymaster, được tạo thành từ hai thành phần, một hợp đồng VerifyingPaymaster (https://github.com/iotexproject/account-abstraction-contracts/blob/main/contracts/paymaster/VerifyingPaymaster.sol) và một điểm dịch vụ ngoài chuỗi để tạo chứng cứ thanh toán cho hợp đồng paymaster (https://paymaster.testnet.w3bstream.com, chỉ cho Testnet).
Mã bên dưới cho bạn biết cách tương tác với việc thực hiện tài khoản p256 từ một khách hàng javascript nhằm tạo một tài khoản:
async function main() {
// tải các hợp đồng đã triển khai
const factory = (await ethers.getContract("P256AccountFactory")) as P256AccountFactory
const entryPoint = (await ethers.getContract("EntryPoint")) as EntryPoint
// một tài khoản EOA để gửi Thao Tác Người Dùng
const bundler = new ethers.Wallet(process.env.BUNDLER!, ethers.provider)
// tải bộ khóa secp256r1
const keyContent = fs.readFileSync(path.join(__dirname, "key.pem"))
const keyPair = ecPem.loadPrivateKey(keyContent)
const publicKey = "0x" + keyPair.getPublicKey("hex").substring(2)
const index = 0
const account = await factory.getAddress(publicKey, index)
// tạo Thao Tác Người Dùng tạo tài khoản
const initCode = hexConcat([ factory.address, factory.interface.encodeFunctionData("createAccount", [publicKey, index]),
])
const createOp = {
sender: account,
initCode: initCode,
}
const fullCreateOp = await fillUserOp(createOp, entryPoint)
// đặt cược IOTX cho gas
const stake = await entryPoint.balanceOf(account)
if (stake.isZero()) {
console.log(`nạp gas cho tài khoản ${account}`)
const tx = await entryPoint
.connect(bundler)
.depositTo(account, { value: ethers.utils.parseEther("10") })
await tx.wait()
}
// ký Thao Tác Người Dùng bằng đường cong secp256r1
const chainId = (await ethers.provider.getNetwork()).chainId
const signedOp = await signOp(
fullCreateOp,
entryPoint.address,
chainId,
new P2565Signer(keyPair)
)
// mô phỏng Thao Tác Người Dùng
const err = await entryPoint.callStatic.simulateValidation(signedOp).catch((e) => e)
if (err.errorName === "FailedOp") {
console.error(`mô phỏng lỗi thao tác ${err.errorArgs.at(-1)}`)
return
} else if (err.errorName !== "ValidationResult") {
console.error(`lỗi không xác định ${err}`)
return
}
console.log(`mô phỏng thao tác thành công`)
// gửi Thao Tác Người D dùng đến EntryPoint
const tx = await entryPoint.connect(bundler).handleOps([signedOp], bundler.address)
console.log(`giao dịch tạo tài khoản: ${tx.hash}, tài khoản: ${account}`)
}
Trong khi mã sau đây sẽ cho bạn biết cách chuyển IOTX sử dụng dịch vụ bundler và paymaster:
async function main() {
const factory = (await ethers.getContract("P256AccountFactory")) as P256AccountFactory
const accountTpl = await ethers.getContractFactory("P256Account")
const entryPoint = (await ethers.getContract("EntryPoint")) as EntryPoint
const paymaster = await ethers.getContract("VerifyingPaymaster")
const bundler = new JsonRpcProvider("http://localhost:4337")
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!)
const keyContent = fs.readFileSync(path.join(__dirname, "key.pem"))
const keyPair = ecPem.loadPrivateKey(keyContent)
const publicKey = "0x" + keyPair.getPublicKey("hex").substring(2)
const index = 0
const account = await factory.getAddress(publicKey, index)
const callData = accountTpl.interface.encodeFunctionData("execute", [
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
ethers.utils.parseEther("0.1"),
"0x",
])
const transferOp = {
sender: account,
callData,
preVerificationGas: 50000,
}
const fullCreateOp = await fillUserOp(transferOp, entryPoint)
fullCreateOp.paymasterAndData = hexConcat([
paymaster.address,
defaultAbiCoder.encode(["uint48", "uint48"], [0, 0]),
"0x" + "00".repeat(65),
])
const validAfter = Math.floor(new Date().getTime() / 1000)
const validUntil = validAfter + 86400 // một ngày
const pendingOpHash = await paymaster.getHash(fullCreateOp, validUntil, validAfter)
const paymasterSignature = await signer.signMessage(arrayify(pendingOpHash))
fullCreateOp.paymasterAndData = hexConcat([
paymaster.address,
defaultAbiCoder.encode(["uint48", "uint48"], [validUntil, validAfter]),
paymasterSignature,
])
const chainId = (await ethers.provider.getNetwork()).chainId
const signedOp = await signOp(
fullCreateOp,
entryPoint.address,
chainId,
new P2565Signer(keyPair)
)
const err = await entryPoint.callStatic.simulateValidation(signedOp).catch((e) => e)
if (err.errorName === "FailedOp") {
console.error(`mô phỏng thao tác lỗi ${err.errorArgs.at(-1)}`)
return
} else if (err.errorName !== "ValidationResult") {
console.error(`lỗi không xác định ${err}`)
return
}
console.log(`mô phỏng thao tác thành công`)
const hexifiedUserOp = deepHexlify(await resolveProperties(signedOp))
const result = await bundler.send("eth_sendUserOperation", [hexifiedUserOp, entryPoint.address])
console.log(`chuyển nhượng sử dụng dịch vụ bundler thành công opHash: ${result}`)
}
Phần còn lại của ví dụ về cách tương tác với việc thực hiện tài khoản p256 từ một khách hàng javascript, có thể được tìm thấy tại https://github.com/iotexproject/account-abstraction-contracts/tree/main/scripts/secp256r1