Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ object AppConfig {

fun showTXWarning() = isDev() || isTesting() || (config().getFeatures().txWarning ?: false)

fun coverBridgeFee() = config().getFeatures().coverBridgeFee ?: false

fun bridgeFeePayer() = if (isTestnet()) config().getBridgeFeePayer().testnet else config().getBridgeFeePayer().mainnet

fun addressRegistry(network: Int): Map<String, String> {
return when (network) {
NETWORK_TESTNET -> flowAddressRegistry().testnet
Expand Down Expand Up @@ -145,20 +149,32 @@ private data class Config(
prod.payer
}
}

fun getBridgeFeePayer(): Payer {
return if (isStagingVersion()) {
staging.bridgeFeePayer
} else {
prod.bridgeFeePayer
}
}
}

private data class Prod(
@SerializedName("features")
val features: Features,
@SerializedName("payer")
val payer: Payer
val payer: Payer,
@SerializedName("bridgeFeePayer")
val bridgeFeePayer: Payer
)

private data class Staging(
@SerializedName("features")
val features: Features,
@SerializedName("payer")
val payer: Payer
val payer: Payer,
@SerializedName("bridgeFeePayer")
val bridgeFeePayer: Payer
)

private data class Features(
Expand All @@ -177,7 +193,9 @@ private data class Features(
@SerializedName("nft_transfer")
val nftTransfer: Boolean?,
@SerializedName("tx_warning_prediction")
val txWarning: Boolean?
val txWarning: Boolean?,
@SerializedName("cover_bridge_fee")
val coverBridgeFee: Boolean?,
)

private data class Payer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package com.flowfoundation.wallet.manager.flowjvm
import com.flowfoundation.wallet.BuildConfig
import com.flowfoundation.wallet.manager.coin.FlowCoin
import com.flowfoundation.wallet.manager.coin.formatCadence
import com.flowfoundation.wallet.manager.config.AppConfig
import com.flowfoundation.wallet.manager.config.NftCollection
import com.flowfoundation.wallet.manager.flow.CadenceScriptBuilder
import com.flowfoundation.wallet.manager.flow.FlowCadenceApi
import com.flowfoundation.wallet.manager.flowjvm.transaction.sendBridgeTransaction
import com.flowfoundation.wallet.manager.flowjvm.transaction.sendTransaction
import com.flowfoundation.wallet.manager.wallet.WalletManager
import com.flowfoundation.wallet.mixpanel.MixpanelManager
Expand Down Expand Up @@ -492,9 +494,16 @@ suspend fun cadenceBridgeNFTToEvm(
nftId: String
): String? {
logd(TAG, "cadenceBridgeNFTToEvm")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_TO_EVM_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
}
}
logd(TAG, "cadenceBridgeNFTToEvm transactionId:$transactionId")
return transactionId
Expand All @@ -505,9 +514,16 @@ suspend fun cadenceBridgeNFTListToEvm(
nftIdList: List<String>
): String? {
logd(TAG, "cadenceBridgeNFTListToEvm")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_LIST_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint64(it) }) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_LIST_TO_EVM_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint64(it) }) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_LIST_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint64(it) }) }
}
}
logd(TAG, "cadenceBridgeNFTListToEvm transactionId:$transactionId")
return transactionId
Expand All @@ -518,9 +534,16 @@ suspend fun cadenceBridgeNFTListFromEvm(
nftIdList: List<String>
): String? {
logd(TAG, "cadenceBridgeNFTListFromEvm")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_LIST_FROM_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint256(it) }) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_LIST_FROM_EVM_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint256(it) }) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_LIST_FROM_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { array(nftIdList.map { uint256(it) }) }
}
}
logd(TAG, "cadenceBridgeNFTListFromEvm transactionId:$transactionId")
return transactionId
Expand All @@ -531,9 +554,16 @@ suspend fun cadenceBridgeNFTFromEvm(
nftId: String
): String? {
logd(TAG, "cadenceBridgeNFTFromEvm")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
}
}
logd(TAG, "cadenceBridgeNFTFromEvm transactionId:$transactionId")
return transactionId
Expand Down Expand Up @@ -634,10 +664,18 @@ suspend fun cadenceBridgeNFTFromFlowToEVM(
nftId: String, recipient: String
): String? {
logd(TAG, "cadenceBridgeNFTFromFlowToEVM")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_FROM_FLOW_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
arg { string(recipient) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_FLOW_TO_EVM_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
arg { address(recipient) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_FLOW_TO_EVM.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint64(nftId) }
arg { string(recipient) }
}
}
logd(TAG, "cadenceBridgeNFTFromFlowToEVM transactionId:$transactionId")
return transactionId
Expand All @@ -648,10 +686,18 @@ suspend fun cadenceBridgeNFTFromEVMToFlow(
nftId: String, recipient: String
): String? {
logd(TAG, "cadenceBridgeNFTFromEVMToFlow")
val transactionId = CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM_TO_FLOW.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
arg { address(recipient) }
val transactionId = if (AppConfig.coverBridgeFee()) {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM_TO_FLOW_WITH_PAYER.transactionWithBridgePayer {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
arg { address(recipient) }
}
} else {
CadenceScript.CADENCE_BRIDGE_NFT_FROM_EVM_TO_FLOW.transactionByMainWallet {
arg { string(nftIdentifier) }
arg { uint256(nftId) }
arg { address(recipient) }
}
}
logd(TAG, "cadenceBridgeNFTFromEVMToFlow transactionId:$transactionId")
return transactionId
Expand Down Expand Up @@ -762,6 +808,23 @@ suspend fun String.transactionByMainWallet(arguments: CadenceArgumentsBuilder.()
}
}

suspend fun CadenceScript.transactionWithBridgePayer(arguments: CadenceArgumentsBuilder.() -> Unit): String? {
val walletAddress = WalletManager.wallet()?.walletAddress()?: return null
logd(TAG, "transactionBridge() walletAddress:$walletAddress")
val args = CadenceArgumentsBuilder().apply { arguments(this) }
return try {
sendBridgeTransaction {
args.build().forEach { arg(it) }
walletAddress(walletAddress)
script(this@transactionWithBridgePayer.getScript().addPlatformInfo())
payer(AppConfig.bridgeFeePayer().address)
}
} catch (e: Exception) {
loge(e)
null
}
}

fun String.addPlatformInfo(): String {
return this.replace("<platform_info>", "Android - ${BuildConfig.VERSION_NAME} - ${BuildConfig
.VERSION_CODE} ${devPrefix()}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,17 @@ enum class CadenceScript(val scriptId: String, val type: CadenceScriptType) {
CADENCE_BRIDGE_FT_FROM_EVM("bridgeTokensFromEvmV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_FT_FROM_EVM_TO_FLOW("bridgeTokensFromEvmToFlowV3", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_TO_EVM("bridgeNFTToEvmV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_TO_EVM_WITH_PAYER("bridgeNFTToEvmWithPayer", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_EVM("bridgeNFTFromEvmV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_EVM_WITH_PAYER("bridgeNFTFromEvmWithPayer", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_LIST_TO_EVM("batchBridgeNFTToEvmV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_LIST_TO_EVM_WITH_PAYER("batchBridgeNFTToEvmWithPayer", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_LIST_FROM_EVM("batchBridgeNFTFromEvmV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_LIST_FROM_EVM_WITH_PAYER("batchBridgeNFTFromEvmWithPayer", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_FLOW_TO_EVM("bridgeNFTToEvmAddressV2", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_FLOW_TO_EVM_WITH_PAYER("bridgeNFTToEvmAddressWithPayer", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_EVM_TO_FLOW("bridgeNFTFromEvmToFlowV3", CadenceScriptType.BRIDGE),
CADENCE_BRIDGE_NFT_FROM_EVM_TO_FLOW_WITH_PAYER("bridgeNFTFromEvmToFlowWithPayer", CadenceScriptType.BRIDGE),

// EVM
CADENCE_CREATE_COA_ACCOUNT("createCoaEmpty", CadenceScriptType.EVM),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import com.flowfoundation.wallet.manager.flowjvm.toAsArgument
import com.flowfoundation.wallet.manager.flowjvm.valueString
import com.flowfoundation.wallet.manager.key.CryptoProviderManager
import com.flowfoundation.wallet.mixpanel.MixpanelManager
import com.flowfoundation.wallet.network.BASE_HOST
import com.flowfoundation.wallet.network.functions.FUNCTION_SIGN_AS_BRIDGE_PAYER
import com.flowfoundation.wallet.network.functions.FUNCTION_SIGN_AS_PAYER
import com.flowfoundation.wallet.network.functions.executeHttpFunction
import com.flowfoundation.wallet.utils.logd
Expand Down Expand Up @@ -76,6 +78,44 @@ suspend fun sendTransaction(
}
}

suspend fun sendBridgeTransaction(
builder: TransactionBuilder.() -> Unit,
): String? {
// updateSecurityProvider()
val transactionBuilder = TransactionBuilder().apply { builder(this) }

try {
logd(TAG, "sendBridgeTransaction prepare")
val voucher = prepare(transactionBuilder)

logd(TAG, "sendBridgeTransaction build flow transaction")
var tx = voucher.toFlowTransactionWithBridgePayer()
tx = tx.addFreeBridgeFeeEnvelope()

logd(TAG, "sendBridgeTransaction to flow chain")
val txID = FlowApi.get().sendTransaction(tx).bytes.bytesToHex()
logd(TAG, "transaction id:$${txID}")
vibrateTransaction()
MixpanelManager.cadenceTransactionSigned(
cadence = voucher.cadence.orEmpty(), txId = txID, authorizers = tx.authorizers.map { it.formatted }.toList(),
proposer = tx.proposalKey.address.formatted,
payer = tx.payerAddress.formatted,
isSuccess = true
)
return txID
} catch (e: Exception) {
loge(e)
MixpanelManager.cadenceTransactionSigned(
cadence = transactionBuilder.script.orEmpty(), txId = "",
authorizers = emptyList(),
proposer = transactionBuilder.walletAddress?.toAddress().orEmpty(),
payer = transactionBuilder.payer ?: (if (isGasFree()) AppConfig.payer().address else transactionBuilder.walletAddress).orEmpty(),
isSuccess = false
)
return null
}
}

suspend fun sendTransactionWithMultiSignature(
builder: TransactionBuilder.() -> Unit,
providers: List<CryptoProvider>
Expand Down Expand Up @@ -143,6 +183,19 @@ private suspend fun FlowTransaction.addFreeGasEnvelope(): FlowTransaction {
)
}

private suspend fun FlowTransaction.addFreeBridgeFeeEnvelope(): FlowTransaction {
val response = executeHttpFunction(FUNCTION_SIGN_AS_BRIDGE_PAYER, buildBridgeFeePayerSignable(), BASE_HOST)
logd(TAG, "response:$response")

val sign = Gson().fromJson(response, SignPayerResponse::class.java).envelopeSigs

return addEnvelopeSignature(
FlowAddress(sign.address),
keyIndex = sign.keyId,
signature = FlowSignature(sign.sig)
)
}

private suspend fun prepare(builder: TransactionBuilder): Voucher {
logd(TAG, "prepare builder:$builder")
val account = FlowApi.get().getAccountAtLatestBlock(FlowAddress(builder.walletAddress?.toAddress().orEmpty()))
Expand Down Expand Up @@ -188,6 +241,43 @@ private suspend fun prepareWithMultiSignature(
)
}

fun FlowTransaction.buildBridgeFeePayerSignable(): PayerSignable? {
val payerAccount = FlowApi.get().getAccountAtLatestBlock(payerAddress) ?: return null
val voucher = Voucher(
cadence = script.stringValue,
refBlock = referenceBlockId.base16Value,
computeLimit = gasLimit.toInt(),
arguments = arguments.map { it.toAsArgument() },
proposalKey = ProposalKey(
address = proposalKey.address.base16Value.toAddress(),
keyId = proposalKey.keyIndex,
sequenceNum = proposalKey.sequenceNumber.toInt(),
),
payer = payerAddress.base16Value.toAddress(),
authorizers = authorizers.map { it.base16Value.toAddress() },
payloadSigs = payloadSignatures.map {
Singature(
address = it.address.base16Value.toAddress(),
keyId = it.keyIndex,
sig = it.signature.base16Value,
)
},
envelopeSigs = listOf(
Singature(
address = AppConfig.bridgeFeePayer().address.toAddress(),
keyId = payerAccount.keys.first().id,
)
),
)

return PayerSignable(
transaction = voucher,
message = PayerSignable.Message(
(DomainTag.TRANSACTION_DOMAIN_TAG + canonicalAuthorizationEnvelope).bytesToHex()
)
)
}

fun FlowTransaction.buildPayerSignable(): PayerSignable? {
val payerAccount = FlowApi.get().getAccountAtLatestBlock(payerAddress) ?: return null
val voucher = Voucher(
Expand Down Expand Up @@ -319,6 +409,39 @@ fun Voucher.toFlowTransaction(): FlowTransaction {
return tx
}

fun Voucher.toFlowTransactionWithBridgePayer(): FlowTransaction {
val transaction = this
var tx = flowTransaction {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If it won't break the current implementation, it would be good use to the the Transaction class from flow-kmm instead of the transaction-DSL from flow-JVM in this function. If it causes too many downstream changes I can refactor it on my end in the upcoming PR. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just to create a special Transaction. At present, the logic related to the Transaction is still implemented using the flow-jvm. I think we can replace this whole part of the logic with flow-kmm later.

script { transaction.cadence.orEmpty() }

arguments = transaction.arguments.orEmpty().map { it.toBytes() }.map { FlowArgument(it) }.toMutableList()

referenceBlockId = FlowId(transaction.refBlock.orEmpty())

gasLimit = computeLimit ?: 9999

proposalKey {
address = FlowAddress(transaction.proposalKey.address.orEmpty())
keyIndex = transaction.proposalKey.keyId ?: 0
sequenceNumber = transaction.proposalKey.sequenceNum ?: 0
}

payerAddress = FlowAddress(transaction.payer.orEmpty())

authorizers(mutableListOf(FlowAddress(transaction.proposalKey.address.orEmpty()), payerAddress))
}

val cryptoProvider = CryptoProviderManager.getCurrentCryptoProvider() ?: return tx

tx = tx.addPayloadSignature(
FlowAddress(proposalKey.address.orEmpty()),
keyIndex = proposalKey.keyId ?: 0,
cryptoProvider.getSigner(),
)

return tx
}

/**
* fix: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC
*/
Expand Down
Loading
Loading