Panduan Penting untuk Abstraksi Akun di IoTeX: Panduan Praktis untuk Tanda Tangan p256

Panduan Penting untuk Abstraksi Akun di IoTeX: Panduan Praktis untuk Tanda Tangan p256

Dengan dukungan penuh komunitas kami pada Proposal Peningkatan IoTeX 14, Abstraksi Akun akhirnya hadir di Mainnet dan Testnet IoTeX, dan fitur-fiturnya kini tersedia untuk semua pengembang ekosistem. Jadi, apa itu AA, bagaimana cara kerjanya, dan bagaimana cara menggunakannya di aplikasi berikutnya?

Penyegaran Cepat


Abstraksi Akun (AA) sebagaimana didefinisikan oleh  ERC-4337, "memungkinkan pengguna menggunakan dompet kontrak pintar yang berisi logika verifikasi sewenang-wenang, bukan EOA sebagai akun utama mereka." ERC-4337 memperkenalkan banyak manfaat pengalaman pengguna, terutama memungkinkan orang menggunakan Kontrak Cerdas sebagai akun utama mereka.

ERC-4337 berjalan di atas blockchain dan tidak memerlukan perubahan apa pun pada blockchain itu sendiri. Saat ini, kode Abstraksi Akun IoTeX didasarkan pada versi rilis ERC-4337 0.6.0.

Komponen AA Infra

AA

Komponen infrastruktur AA adalah:

  • Layanan Bundler: satu titik akhir untuk Mainnet (https://bundler.w3bstream.com) dan satu untuk Testnet (https://bundler.testnet.w3bstream.com). Bundel adalah node offchain yang menggabungkan beberapa operasi pengguna yang diabstraksi menjadi satu transaksi yang dapat diproses oleh blockchain yang mendasarinya. Transaksi ini dikirim ke komponen tetap lainnya, yang disebut Kontrak EntryPoint.
  • Kontrak EntryPoint: Ada dua kontrak EntryPoint yang diterapkan di IoTeX, satu untuk Mainnet (0xc3527348De07d591c9d567ce1998eFA2031B8675) dan satu lagi untuk Testnet (0xc3527348De07d591c9d567ce1998eFA2031B8675). Kontrak EntryPoint bertugas membuat/menerapkan kontrak khusus tertentu, yang disebut kontrak AccountFactory, yang pada gilirannya bertugas membuat akun (kontrak dompet) tertentu yang dapat digunakan untuk tujuan tertentu.

Untuk menggunakan abstraksi akun untuk membuat akun kustom baru, ada komponen tertentu yang harus dibuat oleh pengembang dApp berdasarkan kebutuhan aplikasinya:

  • Kontrak akun, yang mengimplementasikan logika validasi dalam metode validasiUserOp, dan logika eksekusi apa pun yang diperlukan oleh operasi pengguna.
  • Kontrak AccountFactory, yang bertanggung jawab, seperti disebutkan di atas, untuk membuat/menerapkan kontrak akun khusus baru.
  • Beberapa kode klien yang membangun operasi pengguna yang kompatibel dengan aturan verifikasi yang diterapkan di AccountFactory.
  • Paymaster adalah bagian opsional dari arsitektur AA. IoTeX menyediakan layanan paymaster untuk Testnet hanya di https://paymaster.testnet.w3bstream.com. Peran pemberi pembayaran adalah mensponsori bahan bakar yang diperlukan untuk melaksanakan operasi pengguna, baik mensponsori mereka sepenuhnya atau mengizinkan pengguna membayarnya dalam berbagai token.

Contoh: P256AccountFactory


Sebagai contoh pertama, kami telah menyediakan kontrak resmi P256AccountFactory (Mainnet 0xD98d2B6cBca981c777037c5784721d8179D7030b dan Testnet 0x508Db1A73FcBA98594679aD4f5d8D0B880BbdaFB) yang memungkinkan pengembang membuat kontrak akun yang dapat memverifikasi pengguna operasi yang ditandatangani dengan kriptografi "p256", bukan dengan elips "secp256k1" asli Ethereum dan IoTeX melengkung. Hal ini sangat berguna, karena memberdayakan pengembang untuk membuat aplikasi di mana pengguna dapat, misalnya, menandatangani transaksi dengan biometrik mereka, atau beralih dari frase awal, atau bahkan memiliki keamanan yang unggul ketika perangkat mereka mendukung chip keamanan khusus (misalnya Elemen Aman Android dan Enklave Aman Apple, dll.). Kode sumber P256AccountFactory dapat ditemukan di https://github.com/iotexproject/account-abstraction-contracts/blob/main/contracts/accounts/secp256r1/P256AccountFactory.sol sedangkan kontrak Abstraksi Akun sumber terbuka bergantung pada implementasi oleh penulis asli EIP-4337 untuk Ethereum di sini https://github.com/iotexproject/account-abstraction-contracts/tree/main.

P256AccountFactory juga mendukung pengelolaan layanan paymaster, yang terdiri dari dua komponen, kontrak VerifyingPaymaster (https://github.com/iotexproject/account-abstraction-contracts/blob/main/contracts/paymaster/VerifyingPaymaster.sol ) dan titik akhir layanan off-chain untuk menghasilkan bukti pembayaran untuk kontrak paymaster (https://paymaster.testnet.w3bstream.com, hanya untuk Testnet).

Kode di bawah ini menunjukkan cara berinteraksi dengan implementasi akun p256 dari klien javascript untuk membuat akun:

async function main() {
    // load deployed contracts
    const factory = (await ethers.getContract("P256AccountFactory")) as P256AccountFactory
    const entryPoint = (await ethers.getContract("EntryPoint")) as EntryPoint

    // an EOA account for send UserOperations
    const bundler = new ethers.Wallet(process.env.BUNDLER!, ethers.provider)

    // load secp256r1 keypair
    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)

    // create create account UserOperation
    const initCode = hexConcat([        factory.address,        factory.interface.encodeFunctionData("createAccount", [publicKey, index]),
    ])
    const createOp = {
        sender: account,
        initCode: initCode,
    }

    const fullCreateOp = await fillUserOp(createOp, entryPoint)

    // stake IOTX for gas
    const stake = await entryPoint.balanceOf(account)
    if (stake.isZero()) {
        console.log(`deposit gas for account ${account}`)
        const tx = await entryPoint
            .connect(bundler)
            .depositTo(account, { value: ethers.utils.parseEther("10") })
        await tx.wait()
    }

    // sign UserOperation using secp256r1 curve
    const chainId = (await ethers.provider.getNetwork()).chainId
    const signedOp = await signOp(
        fullCreateOp,
        entryPoint.address,
        chainId,
        new P2565Signer(keyPair)
    )

    // simulate UserOperation
    const err = await entryPoint.callStatic.simulateValidation(signedOp).catch((e) => e)
    if (err.errorName === "FailedOp") {
        console.error(`simulate op error ${err.errorArgs.at(-1)}`)
        return
    } else if (err.errorName !== "ValidationResult") {
        console.error(`unknow error ${err}`)
        return
    }
    console.log(`simulate op success`)

    // send UserOpersion to EntryPoint
    const tx = await entryPoint.connect(bundler).handleOps([signedOp], bundler.address)
    console.log(`create account tx: ${tx.hash}, account: ${account}`)
}

Sedangkan kode berikut akan menunjukkan cara mentransfer IOTX menggunakan layanan bundler dan 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 // one day
    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(`simulate op error ${err.errorArgs.at(-1)}`)
        return
    } else if (err.errorName !== "ValidationResult") {
        console.error(`unknow error ${err}`)
        return
    }
    console.log(`simulate op success`)

    const hexifiedUserOp = deepHexlify(await resolveProperties(signedOp))
    const result = await bundler.send("eth_sendUserOperation", [hexifiedUserOp, entryPoint.address])
    console.log(`transfer use bundler success opHash: ${result}`)

Contoh selanjutnya tentang cara berinteraksi dengan implementasi akun p256 dari klien javascript, dapat ditemukan di https://github.com/iotexproject/account-abstraction-contracts/tree/main/scripts/secp256r1

Read more