メインコンテンツまでスキップ

Gas Fee Delegation

Kaia Wave Builders 向け Kaia フィーデリゲーションプログラム

はじめに

Kaia ネットワークのフィーデリゲーションは、別のアカウントがトランザクションのガス代を肩代わりできる機能です。これにより、ユーザーはトランザクションのガス代として自身の $KAIA を消費することなくトランザクションを実行できます。

申請フォーム >

コンセプト

Kaia フィーデリゲーションは 3 つの主要コンポーネントで構成されます。

  1. Sender: トランザクションを送信するアカウント(from)
  2. Unifi Apps: ユーザーが署名したトランザクションをフィーペイヤーサーバーへ中継するリレーヤー
  3. Fee payer server: フィーペイヤーとして署名し、署名済みトランザクションをネットワークへ送信して、呼び出し元へレシートを返却するサーバー

Kaia フィーデリゲーションの動作は以下のとおりです。

  1. Sender がトランザクションを作成します。
  2. Sender がトランザクションにフィーペイヤーのアドレスを指定します。
  3. Sender がトランザクションに署名します。
  4. Sender が署名済みトランザクションを Unifi Apps のバックエンドへ送信します。
  5. バックエンドがフィーペイヤーサーバーにトランザクションの署名をリクエストします。
  6. フィーペイヤーサーバーがフィーペイヤーとしてトランザクションに署名し、ネットワークへ送信してトランザクションのレシートを取得し、トランザクションレシートを返却します。

コードサンプル

前提条件

  1. SDK
    1. kaia-sdk パッケージの一つである ethers-ext をインストールする必要があります。
    2. ethers-ext getting-started
  2. Wallet
    1. fee-delegation は以下でのみサポートされます
      1. kaia-wallet mobile/extension、Unifi-wallet liff/web
    2. Unifi Apps のフロントエンドはウォレット経由でユーザーが署名したトランザクションを取得する必要があります

Web アプリケーションのコードスニペット

Frontend (React) - ユーザーが署名したトランザクションの取得

fee-delegated value transfer example

import { Web3Provider } from "@kaiachain/ethers-ext";
import { TxType, parseKaia } from "@kaiachain/js-ext-core";
import DappPortalSDK from "@linenext/dapp-portal-sdk";
import { useEffect, useState } from "react";
 
// testnet
const feePayer = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
// mainnet: "0x22a4ebd6c88882f7c5907ec5a2ee269fecb5ed7a"
 
const clientId = "30e…b89";
const chainId = "1001";
 
export default function home() {
 const [provider, setProvider] = useState<any>(null);
 const [accounts, setAccounts] = useState<string[]>([]);
 
 const initProvider = async () => {
   const sdk = await DappPortalSDK.init({
     clientId,
     chainId,
   });
   const provider = new Web3Provider(sdk.getWalletProvider());
   const accounts = await provider.send("kaia_requestAccounts", []);
   setProvider(provider);
   setAccounts(accounts);
 };
 
 const sign = async () => {
   const tx = {
     typeInt: TxType.FeeDelegatedValueTransfer,
     from: accounts[0],
     to: accounts[0],
     value: parseKaia("0.1").toHexString(),
     feePayer,
   };
   const signedTx = await provider.send("kaia_signTransaction", [tx]);
 
   // send the signed tx to backend
   console.log(signedTx);
 };
 
 useEffect(() => {
   initProvider();
 }, []);
 
 return (
   <div>
     <button onClick={sign}>Get user signed tx</button>
   </div>
 );
}

fee-delegated smart contract execution example

import { Web3Provider } from "@kaiachain/ethers-ext";
import { TxType, parseKaia } from "@kaiachain/js-ext-core";
import DappPortalSDK from "@linenext/dapp-portal-sdk";
import { useEffect, useState } from "react";
 
const feePayer = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
const clientId = "30e…b89";
const chainId = "1001";
const contractAddr = "0x95Be48607498109030592C08aDC9577c7C2dD505";
const abi = '[...]' //copy from https://docs.kaia.io/ko/references/sdk/ethers-ext/v6/smart-contract/write/
 
export default function home() {
const [provider, setProvider] = useState<any>(null);
const [accounts, setAccounts] = useState<string[]>([]);
 
const initProvider = async () => {
  const sdk = await DappPortalSDK.init({
    clientId,
    chainId,
  });
  const provider = new Web3Provider(sdk.getWalletProvider());
  const accounts = await provider.send("kaia_requestAccounts", []);
  setProvider(provider);
  setAccounts(accounts);
};
 
const sign = async () => {
  const contract = new ethers.Contract(contractAddr, abi, provider);
  const contractCallData = await contract.increment.populateTransaction();
  const tx = {
    typeInt: TxType.FeeDelegatedSmartContractExecution, // fee delegated smart contract execution
    from: accounts[0],
    to: contractCallData.to,
    input: contractCallData.data,
    value: "0x0",
    feePayer: feePayer,
  };
  const signedTx = await provider.send("kaia_signTransaction", [tx]);
  // send the signed tx to backend
  console.log(signedTx);
};
 
useEffect(() => {
  initProvider();
}, []);
 
return (
  <div>
    <button onClick={sign}>Get user signed tx</button>
  </div>
);
}

backend - feePayer サーバーに 'sign' をリクエスト

  1. input : userSignedTx - RLP エンコードされたトランザクション

  2. return : トランザクションレシート

  3. feePayer サーバー URL

    const { Wallet } = require("@kaiachain/ethers-ext/v6");
    const ethers = require("ethers");

    //testnet
    const feePayerServer = "https://fee-delegation-kairos.kaia.io";
    const provider = new ethers.JsonRpcProvider("https://public-en-kairos.node.kaia.io");

    async function main() {
    /* signed tx from user on frontend side */
    const signedTxRLPEncoded = {
    raw: RLP encoded signed TX hex string
    }
    const response = await fetch(`${feePayerServer}/api/signAsFeePayer`, {
    method: "POST",
    headers: {
    "Content-Type": "application/json",
    },
    body: JSON.stringify({ userSignedTx:signedTxRLPEncoded }),
    });
    const data = await response.json();
    console.log(data);
    }

    main();
    1. https://fee-delegation.kaia.io (mainnet)
      1. コントラクトまたは sender の登録が必要です
    2. https://fee-delegation-kairos.kaia.io (testnet)
  4. 残高チェック

    const checkBalance = async () => {
    // It's an address of contracts or senders you registered for fee delegation
    const address = "0x63d4f17d2a8a729fd050f7679d961b1dfbb1e3af";
    const result = await fetch(`${feePayerServer}/api/balance?address=${address}`);
    const isEnough = (await result.json()).data;
    console.log(isEnough ? "enough balance" : "not enough balance");
    };