From 156ba5729814097bd10546fce62162a03f2ff782 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Wed, 19 Feb 2025 13:40:31 +0900 Subject: [PATCH 01/12] Initial commit --- KIPs/kip-247.md | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 KIPs/kip-247.md diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md new file mode 100644 index 0000000..18c5b53 --- /dev/null +++ b/KIPs/kip-247.md @@ -0,0 +1,167 @@ +--- +kip: 247 +title: Gasless Transaction +author: Ian (@ian0371) and Ollie (@blukat29) +discussions-to: TBU +status: Draft +type: Core +created: 2025-02-18 +--- + +## Abstract + +This KIP introduces Gasless Transaction (GaslessTx) where users can swap whitelisted fungible tokens into KAIA without possessing any KAIA. + +## Motivation + +For users who have onboarded onto KAIA from another blockchain via a bridge, it is possible that they do not possess any KAIA tokens and hold only fungible tokens from the bridged assets. +These users will be unable to perform any on-chain actions unless they acquire KAIA through alternative channels. +This limitation effectively restricts their ability to engage with decentralized applications, execute transactions, or participate in any protocol activities within the KAIA ecosystem. + +To enhance the user experience for those, this KIP introduces a concept of gasless transaction (GaslessTx) where transactions with a specific pattern is recognized as the GaslessTx. +Upon detection of GaslessTx, the block proposer may lend the user KAIA to facilitate the GaslessTx execution. +A gasless swap from Token to KAIA shall pay back the lent amount. + +## Specification + +### Definition of GaslessTx + +There are two types of GaslessTx: GaslessApproveTx, and GaslessSwapTx. + +#### GaslessApproveTx + +A GaslessApproveTx must meet all the following conditions to be inserted to `txpool.queue`: + +- A1: `GaslessApproveTx.to` is a whitelisted ERC-20 token. +- A2: `GaslessApproveTx.data` is `approve(spender, amount)`. +- A3: `spender` is a whitelisted `GaslessSwapRouter`. +- A4: `amount` is a nonzero. + +A GaslessApproveTx must meet all the following conditions to be promoted to `txpool.pending`: + +- AP1: GaslessApproveTx is followed by a GaslessSwapTx of the same sender and the `GaslessSwapTx` can be promoted to `txpool.pending`. + +#### GaslessSwapTx + +A `GaslessSwapTx` must meet all the following conditions to be inserted to `txpool.queue`: + +- S1: `GaslessSwapTx.to` is a whitelisted `GaslessSwapRouter`. +- S2: `GaslessSwapTx.data` is `swapForGas(token, amountIn, amountOut, amountRepay)`. +- S3. `token` is a whitelisted ERC20 token. + +A `GaslessSwapTx` must meet all the following conditions to be promoted to `txpool.pending`: + +- SP1: (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.to=token`. +- SP2: (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.data.amount>=amountIn`. +- SP3: `amountRepay` is the correct value. + - (If GaslessApproveTx exists in `txpool.queue`) `amountRepay = CalcRepayAmount(GaslessApproveTx, GaslessSwapTx)`. + - (otherwise) `amountRepay = amountRepay(GaslessSwapTx)`. + - `amountRepay(GaslessApproveTx, GaslessSwapTx) = R1 + R2 + R3` and `amountRepay(GaslessSwapTx) = R1 + R3` where + - `R1 = LendTx.Fee() = SwapTx.GasPrice() * 21000` + - `R2 = ApproveTx.Fee()` (if exists) + - `R3 = SwapTx.Fee()` + +While GaslessApproveTx must be followed by with GaslessSwapTx in order to be promoted, GaslessSwapTx can exist by itself. + +### Definition of LendTx + +Upon detection of a GaslessTx (either GaslessApproveTx or GaslessSwapTx), the block proposer shall inject a transaction, denoted by LendTx, which transfers KAIA to the GaslessTx sender. LendTx must meet all the following requirements: + +- L1: `LendTx.type` is 0x7802, a Ethereum dynamic fee transaction type. +- L2: `LendTx.from` is the proposer itself. +- L3: `LendTx.to` is `GaslessSwapTx.from` +- L4: `LendTx.value` is `amountLend` + - `amountLend` is `R2 + R3` (from aforementioned `amountRepay` definition). + +### GaslessSwapRouter + +GaslessSwapRouter is a smart contract responsible for token swap and repayment. It must support the following interface: + +``` +interface IKIP247 { + function swapForGas(address token, uint256 amountIn, uint256 minAmountOut, uint256 amountRepay) external; + function addToken(address token, address dex) external; + function removeToken(address token) external; + + function claimCommission() external; + function updateCommissionRate(uint256 _commissionRate) external; + + // view functions + function dexAddress(address token) external view returns (address dex); + function getAmountIn(address token, uint amountOut) external view returns (uint amountIn); +} +``` + +`swapForGas()` must ensure that all the following conditions are satisfied: + +- R1: Sender has enough tokens. That is, `token.balanceOf(msg.sender) >= amountIn`. +- R2: Token is whitelisted and has a corresponding DEX address other than zero. +- R3: `amountReceived >= minAmountOut >= amountRepay` where `amountReceived` is the swap output. + +### TxPool Policy Updates + +#### GaslessTx Validation (validateTx) + +To prevent GaslessTx to be discarded, the validation logic at `txpool.queue` insertion needs to detect GaslessTx and skip the account balance check. + +``` +// txpool.validateTx() +if ... +else: // if tx is NOT feeDelegatedTx + if senderBalance.Cmp(tx.Cost()) < 0: + if tx is GaslessTx: + validation ok + else: + insufficientFund error +``` + +#### Promoting GaslessTx (promoteExecutables) + +GaslessApproveTx cannot be executed unless the corresponding GaslessSwapTx from the same sender is already in the transaction pool. +Conversely, if the token has not been approved, GaslessSwapTx cannot be executed before GaslessApproveTx. +In such cases, both transactions need to be promoted simultaneously. + +``` +// txpool.promoteExecutables() +if tx is GaslessApproveTx: + if GaslessSwapTx is in queue: + promote GaslessApproveTx and GaslessSwapTx to pending + else: + push GaslessApproveTx to queue + +if tx is GaslessSwapTx: + if tx is GaslessApproveTx is in queue: + promote GaslessApproveTx and GaslessSwapTx to pending + else if GaslessSwapTx.nonce == getNonce(user): + push GaslessApproveTx to pending + else if GaslessSwapTx.nonce == getNonce(user) + 1: + promote GaslessSwapTx to queue +``` + +#### Transaction Bundling + +This KIP implements `TxBundlingModule` specified in [KIP-245](https://kips.kaia.io/KIPs/kip-245). These are the possible bundles: + +- LendTx - GaslessApproveTx - GaslessSwapTx +- LendTx - GaslessSwapTx + +``` +ExtractTxBundles(txs, prevBundles): + filter ApproveTx, SwapTx from txs + move ApproveTx (if exists) before SwapTx + inject LendTx before ApproveTx if exists, otherwise before SwapTx +``` + +## Rationale + +### Disabling GaslessTx + +If many validators disabled GaslessTx, how does this affect UX? + +## Backward Compatibility + +## References + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 790588e3421927a93bed7c4d32ea10278129dfa6 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Wed, 19 Feb 2025 14:49:11 +0900 Subject: [PATCH 02/12] Add swapForGas distribution logic --- KIPs/kip-247.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 18c5b53..4db8c46 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -37,6 +37,8 @@ A GaslessApproveTx must meet all the following conditions to be inserted to `txp - A3: `spender` is a whitelisted `GaslessSwapRouter`. - A4: `amount` is a nonzero. +(Note that above statements can be verified solely through a static validation of a transaction.) + A GaslessApproveTx must meet all the following conditions to be promoted to `txpool.pending`: - AP1: GaslessApproveTx is followed by a GaslessSwapTx of the same sender and the `GaslessSwapTx` can be promoted to `txpool.pending`. @@ -49,6 +51,8 @@ A `GaslessSwapTx` must meet all the following conditions to be inserted to `txpo - S2: `GaslessSwapTx.data` is `swapForGas(token, amountIn, amountOut, amountRepay)`. - S3. `token` is a whitelisted ERC20 token. +(Note that above statements can be verified solely through a static validation of a transaction.) + A `GaslessSwapTx` must meet all the following conditions to be promoted to `txpool.pending`: - SP1: (If GaslessApproveTx exists in `txpool.queue`) `GaslessApproveTx.to=token`. @@ -98,6 +102,12 @@ interface IKIP247 { - R2: Token is whitelisted and has a corresponding DEX address other than zero. - R3: `amountReceived >= minAmountOut >= amountRepay` where `amountReceived` is the swap output. +`swapForGas()` must distribute the swap output (`amountReceived`) as follows: + +- The block proposer (`block.coinbase`) receives `amountRepay`. +- User receives `(amountReceived - amountRepay) * (1 - commissionRate)`. +- The commission `(amountReceived - amountRepay) * commissionRate` is stored in the contract for later claim. + ### TxPool Policy Updates #### GaslessTx Validation (validateTx) From aaf7de3499aef63d8bd2158b43aced5d51e3210a Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Wed, 19 Feb 2025 14:59:37 +0900 Subject: [PATCH 03/12] Add rationale --- KIPs/kip-247.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 4db8c46..3c0684a 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -157,6 +157,9 @@ This KIP implements `TxBundlingModule` specified in [KIP-245](https://kips.kaia. ``` ExtractTxBundles(txs, prevBundles): + if GaslessTx is disabled: + return nothing + filter ApproveTx, SwapTx from txs move ApproveTx (if exists) before SwapTx inject LendTx before ApproveTx if exists, otherwise before SwapTx @@ -166,7 +169,9 @@ ExtractTxBundles(txs, prevBundles): ### Disabling GaslessTx -If many validators disabled GaslessTx, how does this affect UX? +If many consensus nodes disabled GaslessTx feature, users may experience increased transaction latency. +However, as long as there are still nodes supporting GaslessTx, the transactions will eventually be executed. +Also, because this feature is enabled by default, few consensus nodes are expected to disable it. ## Backward Compatibility From 42f539a7422ee9bc9177f35e3dce77ee38b249c6 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Wed, 19 Feb 2025 15:01:41 +0900 Subject: [PATCH 04/12] Add backward compatibility --- KIPs/kip-247.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 3c0684a..a7fa1c5 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -175,8 +175,12 @@ Also, because this feature is enabled by default, few consensus nodes are expect ## Backward Compatibility +This does not affect the backward compatibility because this does not involve hardfork. + ## References +- [KIP-245](https://kips.kaia.io/KIPs/kip-245) + ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). From 8e4f1f2a7bb811a5ec348e868dbb24767f719728 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 10:42:15 +0900 Subject: [PATCH 05/12] Add requires --- KIPs/kip-247.md | 1 + 1 file changed, 1 insertion(+) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index a7fa1c5..b2f7377 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -6,6 +6,7 @@ discussions-to: TBU status: Draft type: Core created: 2025-02-18 +requires: 245 --- ## Abstract From 6e1cf6506e30ed213e36cd921dfbcc4c6c4cd507 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 10:42:33 +0900 Subject: [PATCH 06/12] Use V1,V2,V3 --- KIPs/kip-247.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index b2f7377..b7f975b 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -61,10 +61,10 @@ A `GaslessSwapTx` must meet all the following conditions to be promoted to `txpo - SP3: `amountRepay` is the correct value. - (If GaslessApproveTx exists in `txpool.queue`) `amountRepay = CalcRepayAmount(GaslessApproveTx, GaslessSwapTx)`. - (otherwise) `amountRepay = amountRepay(GaslessSwapTx)`. - - `amountRepay(GaslessApproveTx, GaslessSwapTx) = R1 + R2 + R3` and `amountRepay(GaslessSwapTx) = R1 + R3` where - - `R1 = LendTx.Fee() = SwapTx.GasPrice() * 21000` - - `R2 = ApproveTx.Fee()` (if exists) - - `R3 = SwapTx.Fee()` + - `amountRepay(GaslessApproveTx, GaslessSwapTx) = V1 + V2 + V3` and `amountRepay(GaslessSwapTx) = V1 + V3` where + - `V1 = LendTx.Fee() = SwapTx.GasPrice() * 21000` + - `V2 = ApproveTx.Fee()` (if exists) + - `V3 = SwapTx.Fee()` While GaslessApproveTx must be followed by with GaslessSwapTx in order to be promoted, GaslessSwapTx can exist by itself. @@ -76,7 +76,7 @@ Upon detection of a GaslessTx (either GaslessApproveTx or GaslessSwapTx), the bl - L2: `LendTx.from` is the proposer itself. - L3: `LendTx.to` is `GaslessSwapTx.from` - L4: `LendTx.value` is `amountLend` - - `amountLend` is `R2 + R3` (from aforementioned `amountRepay` definition). + - `amountLend` is `V2 + V3` (from aforementioned `amountRepay` definition). ### GaslessSwapRouter From 0d1638be9db672878d5f3e79e657037f84e0cdae Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 10:48:32 +0900 Subject: [PATCH 07/12] Single-hop swap --- KIPs/kip-247.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index b7f975b..6632e69 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -80,7 +80,7 @@ Upon detection of a GaslessTx (either GaslessApproveTx or GaslessSwapTx), the bl ### GaslessSwapRouter -GaslessSwapRouter is a smart contract responsible for token swap and repayment. It must support the following interface: +GaslessSwapRouter is a smart contract responsible for token swap and repayment. It supports only single-hop swaps through a predetermined DEX contract. It must support the following interface: ``` interface IKIP247 { From 0b0a68add8052c65c51788b3cda85926580fcfcb Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 14:35:38 +0900 Subject: [PATCH 08/12] Add stateless validation rationale --- KIPs/kip-247.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 6632e69..f352c8a 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -168,6 +168,16 @@ ExtractTxBundles(txs, prevBundles): ## Rationale +### Stateless transaction validations + +The existing validation logics in transaction pool (`validateTx()`) ensures that the transaction sender has sufficient KAIA to cover the gas fee. +However, this KIP does not propose a comparable validation mechanism for GaslessTxs such as ERC-20 token balance checks. +Instead, it suggests stateless transaction validation logics, specifically A{1-4}, AP1, S{1-3}, SP{1-3}. + +While the computational overhead of performing stateful validations via EVM calls may not be significant, such validations inherently lack reliability. They may become obsolete and ineffective since the state database can change between transaction validation and execution. +To address this, this KIP relies on [KIP-245](https://kips.kaia.io/KIPs/kip-245) where all transactions including LendTx will be excluded from the block when GaslessTx reverts. +Thus, this KIP proposes the most basic format checks of GaslessTx in transaction pool. + ### Disabling GaslessTx If many consensus nodes disabled GaslessTx feature, users may experience increased transaction latency. From 184d4eac9587704b040e2cfda4bb79e18585285f Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 14:35:58 +0900 Subject: [PATCH 09/12] Fix promote/push --- KIPs/kip-247.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index f352c8a..52619c3 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -144,9 +144,9 @@ if tx is GaslessSwapTx: if tx is GaslessApproveTx is in queue: promote GaslessApproveTx and GaslessSwapTx to pending else if GaslessSwapTx.nonce == getNonce(user): - push GaslessApproveTx to pending + promote GaslessApproveTx to pending else if GaslessSwapTx.nonce == getNonce(user) + 1: - promote GaslessSwapTx to queue + push GaslessSwapTx to queue ``` #### Transaction Bundling From f865dc084ecb09e537c688d24a921a0098428d4f Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Thu, 20 Feb 2025 17:25:01 +0900 Subject: [PATCH 10/12] Add nonce checks --- KIPs/kip-247.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 52619c3..9374b33 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -135,18 +135,19 @@ In such cases, both transactions need to be promoted simultaneously. ``` // txpool.promoteExecutables() if tx is GaslessApproveTx: + assert tx.nonce == getNonce(tx.from) if GaslessSwapTx is in queue: - promote GaslessApproveTx and GaslessSwapTx to pending + promote tx and GaslessSwapTx to pending else: - push GaslessApproveTx to queue + push tx to queue if tx is GaslessSwapTx: if tx is GaslessApproveTx is in queue: - promote GaslessApproveTx and GaslessSwapTx to pending - else if GaslessSwapTx.nonce == getNonce(user): - promote GaslessApproveTx to pending - else if GaslessSwapTx.nonce == getNonce(user) + 1: - push GaslessSwapTx to queue + promote tx and GaslessApproveTx to pending + else if tx.nonce == getNonce(tx.from): + promote tx to pending + else if tx.nonce == getNonce(tx.from) + 1: + push tx to queue ``` #### Transaction Bundling From 48c1be1afc699123e23db6341cd79a759aff9fd7 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Fri, 21 Feb 2025 13:33:58 +0900 Subject: [PATCH 11/12] Update KIPs/kip-247.md Co-authored-by: Lewis --- KIPs/kip-247.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index 9374b33..f4f7d97 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -1,7 +1,7 @@ --- kip: 247 title: Gasless Transaction -author: Ian (@ian0371) and Ollie (@blukat29) +author: Ian (@ian0371), Ollie (@blukat29) discussions-to: TBU status: Draft type: Core From e6794d52016952ef54ec42154464b670375905d1 Mon Sep 17 00:00:00 2001 From: Chihyun Song Date: Fri, 21 Feb 2025 13:59:58 +0900 Subject: [PATCH 12/12] Add nonce checks --- KIPs/kip-247.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/KIPs/kip-247.md b/KIPs/kip-247.md index f4f7d97..9fe1223 100644 --- a/KIPs/kip-247.md +++ b/KIPs/kip-247.md @@ -137,12 +137,14 @@ In such cases, both transactions need to be promoted simultaneously. if tx is GaslessApproveTx: assert tx.nonce == getNonce(tx.from) if GaslessSwapTx is in queue: + assert GaslessSwapTx.nonce == getNonce(tx.from) + 1 promote tx and GaslessSwapTx to pending else: push tx to queue if tx is GaslessSwapTx: if tx is GaslessApproveTx is in queue: + assert GaslessApproveTx.nonce == getNonce(tx.from) promote tx and GaslessApproveTx to pending else if tx.nonce == getNonce(tx.from): promote tx to pending