Subscribe

Panduan Penting Account Abstraction di IoTeX: Panduan Praktis untuk Signature p256

Panduan Penting Account Abstraction di IoTeX: Panduan Praktis untuk Signature p256

Dengan komunitas kami yang memberikan suara penuh mendukung IoTeX Improvement Proposal 14, Account Abstraction akhirnya hadir di IoTeX Mainnet dan Testnet, dan fitur-fiturnya kini tersedia bagi semua developer ekosistem. Jadi, apa itu AA, bagaimana cara kerjanya, dan bagaimana Anda dapat menggunakannya dalam aplikasi Anda berikutnya?

Penyegaran Singkat

Account Abstraction (AA) sebagaimana didefinisikan oleh ERC-4337, “memungkinkan pengguna untuk menggunakan smart contract wallet yang berisi logika verifikasi arbitrer alih-alih EOA sebagai akun utama mereka.” ERC-4337 memperkenalkan banyak manfaat pengalaman pengguna, yang paling menonjol adalah memungkinkan orang untuk menggunakan Smart Contract sebagai akun utama mereka.

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

Komponen dari Infrastruktur AA

AA

Komponen-komponen dari infrastruktur AA adalah:

  • Bundler Services: satu endpoint untuk Mainnet (https://bundler.w3bstream.com) dan satu untuk Testnet (https://bundler.testnet.w3bstream.com). Bundler adalah node offchain yang menggabungkan beberapa operasi pengguna yang diabstraksikan menjadi satu transaksi yang dapat diproses oleh blockchain yang mendasarinya. Transaksi ini dikirim ke komponen tetap lainnya, yang disebut Contract EntryPoint.
  • Contract EntryPoint: Ada dua contract EntryPoint yang di-deploy di IoTeX, satu untuk Mainnet (0xc3527348De07d591c9d567ce1998eFA2031B8675) dan satu untuk Testnet (0xc3527348De07d591c9d567ce1998eFA2031B8675). Contract EntryPoint bertugas membuat/men-deploy contract khusus tertentu, yang disebut contract AccountFactory, yang pada gilirannya bertugas membuat akun tertentu (wallet contract) yang dapat digunakan untuk tujuan spesifik.

Untuk menggunakan account abstraction guna membuat akun kustom baru, ada komponen-komponen tertentu yang harus dibuat oleh developer dApp berdasarkan kebutuhan aplikasi mereka:

  • Contract Account, yang mengimplementasikan logika validasi dalam metode validateUserOp, dan logika eksekusi apa pun yang mungkin diperlukan oleh operasi pengguna.
  • Contract AccountFactory, yang bertugas, seperti disebutkan di atas, untuk membuat/men-deploy contract akun kustom baru.
  • Beberapa kode client yang membangun user ops yang kompatibel dengan aturan verifikasi yang diimplementasikan dalam AccountFactory.
  • Paymaster adalah bagian opsional dari arsitektur AA. IoTeX menyediakan layanan paymaster hanya untuk Testnet di https://paymaster.testnet.w3bstream.com. Peran paymaster adalah untuk mensponsori gas yang dibutuhkan untuk mengeksekusi operasi pengguna, baik mensponsorinya sepenuhnya atau memungkinkan pengguna untuk membayarnya dalam berbagai token.

Contoh: P256AccountFactory

Sebagai contoh pertama, kami telah menyediakan contract resmi P256AccountFactory (Mainnet 0xD98d2B6cBca981c777037c5784721d8179D7030b dan Testnet 0x508Db1A73FcBA98594679aD4f5d8D0B880BbdaFB) yang memungkinkan developer membuat contract akun yang dapat memverifikasi operasi pengguna yang ditandatangani dengan kriptografi “p256”, alih-alih dengan kurva eliptik native Ethereum dan IoTeX “secp256k1”. Ini sangat berguna, karena memberdayakan developer untuk membuat aplikasi di mana pengguna dapat, misalnya, menandatangani transaksi dengan biometrik mereka, atau beralih dari seed phrase, atau bahkan memiliki keamanan yang lebih unggul saat perangkat mereka mendukung chip keamanan khusus (misalnya Secure Element milik Android dan Secure Enclave milik Apple, dll.). Kode sumber dari P256AccountFactorycan dapat ditemukan di https://github.com/iotexproject/account-abstraction-contracts/blob/main/contracts/accounts/secp256r1/P256AccountFactory.sol sementara contract Account Abstraction open source bergantung pada implementasi oleh penulis asli EIP-4337 untuk Ethereum di sini https://github.com/iotexproject/account-abstraction-contracts/tree/main.

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

Kode di bawah ini menunjukkan cara berinteraksi dengan implementasi akun p256 dari client 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}`)
}

Sementara 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}`)
}

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

IoTeX