Skip to content

Feat/gasless#6264

Merged
crossle merged 27 commits intomasterfrom
feat/gasless
Apr 10, 2026
Merged

Feat/gasless#6264
crossle merged 27 commits intomasterfrom
feat/gasless

Conversation

@SeniorZhai
Copy link
Copy Markdown
Member

No description provided.

@SeniorZhai SeniorZhai added the testing Now testing, but you can review label Mar 31, 2026
@SeniorZhai SeniorZhai removed the testing Now testing, but you can review label Apr 7, 2026
@SeniorZhai SeniorZhai marked this pull request as ready for review April 7, 2026 05:28
@SeniorZhai SeniorZhai requested a review from Copilot April 7, 2026 05:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “gasless transfer” support to the Web3 send flow by introducing new API endpoints/models, new signing type handling, and updating the wallet confirmation UI to display gasless fees and route gasless submissions.

Changes:

  • Add gasless fee/prepare/submit API requests + responses and expose them via RouteService/Web3Repository/Web3ViewModel.
  • Integrate gasless fee selection + validation into InputFragment, including Solana and EVM submission paths.
  • Update browser wallet bottom sheet + Compose preview to support gasless transfer confirmations (custom fee display + custom PIN action).

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/src/main/java/one/mixin/android/web3/js/Web3Signer.kt Adds gasless signing path for EVM message signing.
app/src/main/java/one/mixin/android/web3/js/JsSignMessage.kt Introduces TYPE_GASLESS_TRANSFER and helper predicate.
app/src/main/java/one/mixin/android/ui/wallet/InputFragment.kt Implements gasless fee selection, validation, and Solana/EVM submission flows.
app/src/main/java/one/mixin/android/ui/wallet/ClassicWalletFragment.kt Refresh job now scoped to non-empty wallet id.
app/src/main/java/one/mixin/android/ui/home/web3/Web3ViewModel.kt Adds gasless API wrappers and centralizes web3 private key derivation.
app/src/main/java/one/mixin/android/ui/home/web3/BrowserWalletBottomSheetDialogFragment.kt Adds fee args + gasless handling and introduces a custom PIN action hook.
app/src/main/java/one/mixin/android/ui/home/web3/BrowserPage.kt Shows gasless preview + fee token/amount display adjustments.
app/src/main/java/one/mixin/android/ui/home/MainActivity.kt Adds (currently unused) gasless/demo helpers and models.
app/src/main/java/one/mixin/android/ui/common/PendingTransactionRefreshHelper.kt Adjusts background refresh behavior for pending tx polling.
app/src/main/java/one/mixin/android/repository/Web3Repository.kt Adds repository calls for gasless endpoints.
app/src/main/java/one/mixin/android/api/service/RouteService.kt Adds web3/gasless/* endpoints.
app/src/main/java/one/mixin/android/api/response/web3/GaslessTxResponse.kt Adds response models for gasless prepare (incl. EVM signing payload).
app/src/main/java/one/mixin/android/api/response/web3/GaslessFeeResponse.kt Adds response models for gasless fee estimates.
app/src/main/java/one/mixin/android/api/request/web3/GaslessFeeRequest.kt Adds request model for fee estimates.
app/src/main/java/one/mixin/android/api/request/web3/GaslessTxRequest.kt Adds request model for gasless prepare.
app/src/main/java/one/mixin/android/api/request/web3/SubmitGaslessTxRequest.kt Adds request model for gasless submit.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 395 to +405
step = Step.Loading
errorInfo = null
customPinAction?.let { action ->
withContext(Dispatchers.Main.immediate) {
action(pin)
step = Step.Done
defaultSharedPreferences.putLong(
Constants.BIOMETRIC_PIN_CHECK,
System.currentTimeMillis(),
)
}
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

customPinAction is invoked inside withContext(Dispatchers.Main.immediate), which forces the provided suspend callback to run on the main thread. In the gasless flow this callback performs network/signing work, so this can freeze the UI. Run the callback on the existing IO coroutine context and only switch to Main for updating step/preferences/UI state.

Suggested change
step = Step.Loading
errorInfo = null
customPinAction?.let { action ->
withContext(Dispatchers.Main.immediate) {
action(pin)
step = Step.Done
defaultSharedPreferences.putLong(
Constants.BIOMETRIC_PIN_CHECK,
System.currentTimeMillis(),
)
}
withContext(Dispatchers.Main.immediate) {
step = Step.Loading
errorInfo = null
}
customPinAction?.let { action ->
action(pin)
withContext(Dispatchers.Main.immediate) {
step = Step.Done
}
defaultSharedPreferences.putLong(
Constants.BIOMETRIC_PIN_CHECK,
System.currentTimeMillis(),
)

Copilot uses AI. Check for mistakes.
Comment on lines +1876 to +1893
val ethPayload = GsonHelper.customGson.fromJson(payload, EthGaslessTxPayload::class.java)
?: throw IllegalStateException("Failed to parse gasless EVM payload")
val userOpSignature = Web3Signer.signEthMessage(
priv = privateKey,
message = ethPayload.signing.userOperation.message,
type = JsSignMessage.TYPE_GASLESS_TRANSFER,
)
val eip7702AuthSignature = ethPayload.signing.eip7702Auth
?.takeIf { it.required }
?.let { auth ->
if (!auth.address.equals(GASLESS_EIP7702_AUTHORIZED_ADDRESS, ignoreCase = true)) {
throw IllegalArgumentException("Unsupported EIP-7702 auth target")
}
Web3Signer.signEthMessage(
priv = privateKey,
message = auth.message,
type = JsSignMessage.TYPE_GASLESS_TRANSFER,
)
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EthGaslessTxPayload.signing.*.signType is parsed from the backend but currently ignored when producing userOpSignature / eip7702AuthSignature (both are always signed via TYPE_GASLESS_TRANSFER). If the backend varies signType (e.g., personal_sign vs typed data vs raw hash), this will generate invalid signatures. Use signType to select the appropriate signing algorithm (or remove the field if it’s truly constant).

Copilot uses AI. Check for mistakes.
Comment on lines +1013 to +1031
private fun <T> requireMixinData(
response: MixinResponse<T>,
action: String,
): T {
if (!response.isSuccess) {
throw IllegalStateException("$action failed: ${response.errorDescription}")
}
return requireNotNull(response.data) { "$action returned empty data" }
}

private fun requireMixinSuccess(
response: MixinResponse<*>,
action: String,
) {
if (!response.isSuccess) {
throw IllegalStateException("$action failed: ${response.errorDescription}")
}
}

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requireMixinData / requireMixinSuccess are added but not used anywhere in this file. Please either wire them into the new gasless/demo flows or remove them to avoid dead code and future confusion.

Suggested change
private fun <T> requireMixinData(
response: MixinResponse<T>,
action: String,
): T {
if (!response.isSuccess) {
throw IllegalStateException("$action failed: ${response.errorDescription}")
}
return requireNotNull(response.data) { "$action returned empty data" }
}
private fun requireMixinSuccess(
response: MixinResponse<*>,
action: String,
) {
if (!response.isSuccess) {
throw IllegalStateException("$action failed: ${response.errorDescription}")
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +1349 to +1370
private data class Eip7702GaslessDemoArgs(
val from: String?,
val to: String,
val assetId: String,
val amount: String,
val chainId: String,
val feeAssetId: String?,
)

private data class Eip7702GaslessDemoResult(
val from: String,
val to: String,
val assetId: String,
val amount: String,
val chainId: String,
val feeAssetId: String,
val userOpSignType: String,
val userOpSignature: String,
val eip7702Required: Boolean,
val eip7702AuthSignature: String?,
)

Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eip7702GaslessDemoArgs and Eip7702GaslessDemoResult are currently unused. If they’re only for local testing, consider removing them or guarding them behind a debug-only flag/source set so they don’t ship as dead production code.

Suggested change
private data class Eip7702GaslessDemoArgs(
val from: String?,
val to: String,
val assetId: String,
val amount: String,
val chainId: String,
val feeAssetId: String?,
)
private data class Eip7702GaslessDemoResult(
val from: String,
val to: String,
val assetId: String,
val amount: String,
val chainId: String,
val feeAssetId: String,
val userOpSignType: String,
val userOpSignature: String,
val eip7702Required: Boolean,
val eip7702AuthSignature: String?,
)

Copilot uses AI. Check for mistakes.
@crossle crossle merged commit ee66eac into master Apr 10, 2026
2 checks passed
@crossle crossle deleted the feat/gasless branch April 10, 2026 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants