use ffi for mweb (#2456)

* chore: add ffi and ffigen dependencies to pubspec.yaml * refactor: replace method channel with FFI implementation for MWeb module * fix: correct `cd` command in build_mwebd.sh script * chore: add go.sum file with dependency checksums * fix: ensure ANDROID_NDK_VERSION is set before building and dynamically update NDK_BIN path * fix: update NDK_BIN path to dynamically detect platform in build_mwebd.sh script * refactor: remove unused mwebd dependencies and clean up CwMwebPlugin code * Update scripts/android/build_mwebd.sh Co-authored-by: cyan <cyjan@mrcyjanek.net> * refactor: replace iOS MoneroWallet framework with Mwebd.xcframework and clean up plugin implementation [skip-ci] * fix: mwebd ffi ios * refactor: update MWebFfi class to use non-nullable `lib` and improve address debug logging * chore: remove unused iOS/Android build scripts for mwebd * refactor: replace mweb.so with libmweb.so, use sync methods in MWebFfi, and update iOS/Android frameworks/scripts accordingly * chore: unify mwebd build process in workflows, remove unused gomobile init command --------- Co-authored-by: cyan <cyjan@mrcyjanek.net>

Konstantin Ullrich committed Sep 12, 2025 at 15:13 UTC 309ad1836edf48edc2b16842350c74e4bdd05faa
20 files changed +607 -272
.github/workflows/pr_test_build_android.yml
-1
@@ -254,7 +254,6 @@ jobs:
254 export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }')
255 echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment
256 pushd scripts/android
257 - gomobile init;
257 ./build_mwebd.sh --dont-install
258 popd
259
.github/workflows/pr_test_build_linux.yml
+9
@@ -232,6 +232,15 @@ jobs:
232 run: |
233 flutter pub get
234
235 + - name: Build mwebd
236 + run: |
237 + set -x -e
238 + export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }')
239 + echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment
240 + pushd scripts/android
241 + ./build_mwebd.sh --dont-install
242 + popd
243 +
244 - name: Build generated code
245 run: |
246 ./model_generator.sh async
cw_bitcoin/lib/litecoin_wallet_addresses.dart
+1 -3
@@ -6,7 +6,6 @@ import 'package:bitcoin_base/bitcoin_base.dart';
6 import 'package:blockchain_utils/blockchain_utils.dart';
7 import 'package:cw_bitcoin/bitcoin_address_record.dart';
8 import 'package:cw_bitcoin/bitcoin_unspent.dart';
9 -import 'package:cw_bitcoin/electrum_wallet.dart';
9 import 'package:cw_bitcoin/utils.dart';
10 import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
11 import 'package:cw_core/unspent_coin_type.dart';
@@ -84,8 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
83 generating = true;
84 try {
85 while (mwebAddrs.length <= (index + 1)) {
87 - final addresses =
88 - await CwMweb.addresses(scan, spend, mwebAddrs.length, mwebAddrs.length + 50);
86 + final addresses = CwMweb.addresses(scan, spend, mwebAddrs.length, mwebAddrs.length + 50);
87 printV("generated up to index ${mwebAddrs.length}");
88 // sleep for a bit to avoid making the main thread unresponsive:
89 await Future.delayed(Duration(milliseconds: 200));
cw_mweb/android/build.gradle
-12
@@ -21,14 +21,6 @@ allprojects {
21 }
22 }
23
24 -rootProject.allprojects {
25 - repositories {
26 - flatDir {
27 - dirs project(':cw_mweb').file('libs')
28 - }
29 - }
30 -}
31 -
24 apply plugin: 'com.android.library'
25 apply plugin: 'kotlin-android'
26
@@ -74,7 +66,3 @@ android {
66 }
67 }
68 }
77 -
78 -dependencies {
79 - implementation (name: 'mwebd', ext: 'aar')
80 -}
cw_mweb/android/src/main/jniLibs/x86/.gitkeep
cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt
+1 -36
@@ -8,9 +8,6 @@ import io.flutter.plugin.common.MethodChannel
8 import io.flutter.plugin.common.MethodChannel.MethodCallHandler
9 import io.flutter.plugin.common.MethodChannel.Result
10
11 -import mwebd.Mwebd
12 -import mwebd.Server
13 -
11 /** CwMwebPlugin */
12 class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
13 /// The MethodChannel that will the communication between Flutter and native Android
@@ -18,8 +15,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
15 /// This local reference serves to register the plugin with the Flutter Engine and unregister it
16 /// when the Flutter Engine is detached from the Activity
17 private lateinit var channel : MethodChannel
21 - private var server: Server? = null
22 - private var port: Long? = null
18
19 override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
20 channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb")
@@ -27,40 +22,10 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
22 }
23
24 override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
30 - if (call.method == "start") {
31 - server?.stop()
32 - val dataDir = call.argument("dataDir") ?: ""
33 - val nodeUri = call.argument("nodeUri") ?: ""
34 - server = server ?: Mwebd.newServer("", dataDir, nodeUri)
35 - port = server?.start(0)
36 - result.success(port)
37 - } else if (call.method == "stop") {
38 - server?.stop()
39 - server = null
40 - port = null
41 - result.success(null)
42 - } else if (call.method == "address") {
43 - // val scanSecret: ByteArray = call.argument<ByteArray>("scanSecret") ?: ByteArray(0)
44 - // val spendPub: ByteArray = call.argument<ByteArray>("spendPub") ?: ByteArray(0)
45 - // val index: Int = call.argument<Int>("index") ?: 0
46 - // val res = Mwebd.address(scanSecret, spendPub, index)
47 - // result.success(res)
48 - } else if (call.method == "addresses") {
49 - val scanSecret: ByteArray = call.argument<ByteArray>("scanSecret") ?: ByteArray(0)
50 - val spendPub: ByteArray = call.argument<ByteArray>("spendPub") ?: ByteArray(0)
51 - val fromIndex: Int = call.argument<Int>("fromIndex") ?: 0
52 - val toIndex: Int = call.argument<Int>("toIndex") ?: 0
53 - val res = Mwebd.addresses(scanSecret, spendPub, fromIndex, toIndex)
54 - result.success(res)
55 - } else {
56 - result.notImplemented()
57 - }
25 + result.notImplemented()
26 }
27
28 override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
29 channel.setMethodCallHandler(null)
62 - server?.stop()
63 - server = null
64 - port = null
30 }
31 }
cw_mweb/ffigen_config.yaml new
+8
@@ -0,0 +1,8 @@
1 +# yaml-language-server: $schema=../../ffigen.schema.json
2 +
3 +name: MWebFlutter
4 +description: Bindings to `go/mweb.h`.
5 +output: 'lib/generated_bindings.g.dart'
6 +headers:
7 + entry-points:
8 + - 'android/src/main/jniLibs/arm64-v8a/libmweb.h'
cw_mweb/go/go.mod new
+50
@@ -0,0 +1,50 @@
1 +module github.com/cake-tech/cake_wallet/cw_mweb/go
2 +
3 +go 1.24.2
4 +
5 +require (
6 + github.com/Microsoft/go-winio v0.6.2 // indirect
7 + github.com/StackExchange/wmi v1.2.1 // indirect
8 + github.com/aead/siphash v1.0.1 // indirect
9 + github.com/alitto/pond/v2 v2.2.0 // indirect
10 + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect
11 + github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect
12 + github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect
13 + github.com/davecgh/go-spew v1.1.1 // indirect
14 + github.com/deckarep/golang-set/v2 v2.6.0 // indirect
15 + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect
16 + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
17 + github.com/decred/dcrd/lru v1.1.2 // indirect
18 + github.com/ethereum/go-ethereum v1.14.8 // indirect
19 + github.com/go-ole/go-ole v1.3.0 // indirect
20 + github.com/gorilla/websocket v1.4.2 // indirect
21 + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
22 + github.com/holiman/uint256 v1.3.1 // indirect
23 + github.com/kkdai/bstream v1.0.0 // indirect
24 + github.com/klauspost/cpuid/v2 v2.2.7 // indirect
25 + github.com/ltcmweb/coinswapd v0.1.0 // indirect
26 + github.com/ltcmweb/ltcd v0.25.6 // indirect
27 + github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 // indirect
28 + github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 // indirect
29 + github.com/ltcmweb/mwebd v0.1.8 // indirect
30 + github.com/ltcmweb/neutrino v0.17.4 // indirect
31 + github.com/ltcmweb/neutrino/cache v1.1.0 // indirect
32 + github.com/ltcmweb/secp256k1 v0.1.1 // indirect
33 + github.com/ltcsuite/lnd/queue v1.1.0 // indirect
34 + github.com/ltcsuite/lnd/ticker v1.1.0 // indirect
35 + github.com/ltcsuite/ltcwallet/walletdb v1.3.5 // indirect
36 + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
37 + github.com/tklauser/go-sysconf v0.3.12 // indirect
38 + github.com/tklauser/numcpus v0.6.1 // indirect
39 + go.etcd.io/bbolt v1.3.10 // indirect
40 + golang.org/x/crypto v0.33.0 // indirect
41 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
42 + golang.org/x/net v0.35.0 // indirect
43 + golang.org/x/sys v0.30.0 // indirect
44 + golang.org/x/text v0.22.0 // indirect
45 + google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 // indirect
46 + google.golang.org/grpc v1.63.2 // indirect
47 + google.golang.org/protobuf v1.34.2 // indirect
48 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
49 + lukechampine.com/blake3 v1.3.0 // indirect
50 +)
cw_mweb/go/go.sum new
+97
@@ -0,0 +1,97 @@
1 +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
2 +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
3 +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
4 +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
5 +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg=
6 +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
7 +github.com/alitto/pond/v2 v2.2.0 h1:hX3B1Lu4b5PjSHR+IWNRDKD0Jfw2ew8V25J7Vu5j7RM=
8 +github.com/alitto/pond/v2 v2.2.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE=
9 +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
10 +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
11 +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw=
12 +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
13 +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
14 +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
15 +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
16 +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
18 +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
19 +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
20 +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
21 +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
22 +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
23 +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY=
24 +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8=
25 +github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig=
26 +github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs=
27 +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
28 +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
29 +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
30 +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
31 +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
32 +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
33 +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
34 +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs=
35 +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
36 +github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
37 +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
38 +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
39 +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
40 +github.com/ltcmweb/coinswapd v0.1.0 h1:M6eoz4g7fDpsQeNA8wWbBh6PY1xykrihyLU44SgljeI=
41 +github.com/ltcmweb/coinswapd v0.1.0/go.mod h1:CY5cVSympaLrMO8fHWXO+e/iuPLs2L0U9daMCRTstEM=
42 +github.com/ltcmweb/ltcd v0.25.6 h1:96aC38ZEyNVztylrBBqYnVZJ6cuF7IUvL2knhbMxs3Y=
43 +github.com/ltcmweb/ltcd v0.25.6/go.mod h1:jQbvPfnT4bBXJKRwU5SdD9ZUopwmSvhngdjeopv2cIU=
44 +github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 h1:gJc1ljDPCBtwBKFcC4SW44BFoJqoZzkeagndMYdqKKE=
45 +github.com/ltcmweb/ltcd/btcec/v2 v2.3.3/go.mod h1:NRr2WtpiSiSO29TkdZhbGNRA/Q16DV2eNbORf+M2ykI=
46 +github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 h1:CAPyzHI3bCFRrrVHZkDUR2i3Awj6l/aqAZIEi0E/nfM=
47 +github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3/go.mod h1:zB+HhI2IbIwTGpdAhdzpm1GVX4ShcWTgN42+ar9HXrg=
48 +github.com/ltcmweb/mwebd v0.1.8 h1:5TJgrM76H3s6kWUJ6CsU1L+DaafcAZ1edDlkyJ+GWt0=
49 +github.com/ltcmweb/mwebd v0.1.8/go.mod h1:KPf+lXEvEMuG0NaHume0imWMb9Cvr15SNPujZO6EAv4=
50 +github.com/ltcmweb/neutrino v0.17.4 h1:ZsguBvicTM5CtZDsKg+OBFuPUCKjfEJ3lRSGXPjF2oM=
51 +github.com/ltcmweb/neutrino v0.17.4/go.mod h1:KktZ+McIwHuTHe0K8JK9Wv4qgv8orkA21a0ZOU4SwJ4=
52 +github.com/ltcmweb/neutrino/cache v1.1.0 h1:C0Qn2p8ogcskRPfrBKPUayjn3m/CUJqI7otopNScvhk=
53 +github.com/ltcmweb/neutrino/cache v1.1.0/go.mod h1:HddBIjnWaEvrDSnpMIBKkxCJaoRMlg2DfVdNFM0y+ME=
54 +github.com/ltcmweb/secp256k1 v0.1.1 h1:hsGpnFcZ5zmjSxg7AswZp8ygo5NiY06OYWJk97hFizI=
55 +github.com/ltcmweb/secp256k1 v0.1.1/go.mod h1:Kdj2WHIAc0Y1apPtVY7nVxImRWiRTQue2vs1gt1VoEc=
56 +github.com/ltcsuite/lnd/queue v1.1.0 h1:/aVgox4Lz74xBU8BSw5HDau7hHl2irJs5M9u1SPQ2E0=
57 +github.com/ltcsuite/lnd/queue v1.1.0/go.mod h1:DJrxK2gPC2FjJAVYxPOcnY2CplI3rhL2PEq7IexlTWQ=
58 +github.com/ltcsuite/lnd/ticker v1.0.1/go.mod h1:WZKpekfDVAVv7Gsrr0GAWC/U1XURfGesFg9sQYJbeL4=
59 +github.com/ltcsuite/lnd/ticker v1.1.0 h1:3zYM/JlKNqq+DotW8jiEdHVeY5Yl7n7cUb2bTof1yXM=
60 +github.com/ltcsuite/lnd/ticker v1.1.0/go.mod h1:K2qQ3EPe8enztYvn/VAbLafCPy13XHMuCRPsySoAbt8=
61 +github.com/ltcsuite/ltcwallet/walletdb v1.3.5 h1:WymVw0FBQ8KJgH7B88ujRqBOJ9R0en9K9urpJW4atAE=
62 +github.com/ltcsuite/ltcwallet/walletdb v1.3.5/go.mod h1:29SBzxA55wNxY3ctFw6t5PgsULwf3NMwg2MiGQgtrJE=
63 +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
64 +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
65 +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
66 +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
67 +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
68 +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
69 +go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
70 +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
71 +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
72 +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
73 +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
74 +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
75 +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
76 +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
77 +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
78 +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
79 +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
80 +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
81 +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
82 +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
83 +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
84 +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
85 +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
86 +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
87 +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
88 +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 h1:umK/Ey0QEzurTNlsV3R+MfxHAb78HCEX/IkuR+zH4WQ=
89 +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
90 +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
91 +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
92 +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
93 +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
94 +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
95 +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
96 +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
97 +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
cw_mweb/go/mweb.go new
+58
@@ -0,0 +1,58 @@
1 +package main
2 +
3 +import (
4 + "C"
5 + "unsafe"
6 +
7 + "github.com/ltcmweb/mwebd"
8 +)
9 +
10 +var server *mwebd.Server
11 +
12 +//export StartServer
13 +func StartServer(chain *C.char, dataDir *C.char, nodeUri *C.char) C.int {
14 + if server != nil {
15 + server.Stop()
16 + }
17 + goChain := C.GoString(chain)
18 + goDataDir := C.GoString(dataDir)
19 + goNodeUri := C.GoString(nodeUri)
20 +
21 + var err error
22 + server, err = mwebd.NewServer(goChain, goDataDir, goNodeUri)
23 + if err != nil {
24 + return 0
25 + }
26 +
27 + start, err := server.Start(0)
28 + if err != nil {
29 + return 0
30 + }
31 +
32 + return C.int(start)
33 +}
34 +
35 +//export StopServer
36 +func StopServer() {
37 + if server == nil {
38 + return
39 + }
40 + server.Stop()
41 + server = nil
42 +}
43 +
44 +//export Addresses
45 +func Addresses(scanSecret *C.char, scanSecretLen C.int, spendPubKey *C.char, spendPubKeyLen C.int, i C.int, j C.int) *C.char {
46 + // Convert C pointers to Go byte slices
47 + goScanSecret := C.GoBytes(unsafe.Pointer(scanSecret), scanSecretLen)
48 + goSpendPubKey := C.GoBytes(unsafe.Pointer(spendPubKey), spendPubKeyLen)
49 +
50 + // Call the original Go function
51 + addr := mwebd.Addresses(goScanSecret, goSpendPubKey, int32(i), int32(j))
52 +
53 + // Convert the resulting Go string to a C string
54 + // The C code must free this memory
55 + return C.CString(addr)
56 +}
57 +
58 +func main() {}
cw_mweb/ios/Classes/CwMwebPlugin.swift
+1 -86
@@ -1,6 +1,5 @@
1 import Flutter
2 import UIKit
3 -import Mwebd
3
4 public class CwMwebPlugin: NSObject, FlutterPlugin {
5 public static func register(with registrar: FlutterPluginRegistrar) {
@@ -9,92 +8,8 @@ public static func register(with registrar: FlutterPluginRegistrar) {
8 registrar.addMethodCallDelegate(instance, channel: channel)
9 }
10
12 - private static var server: MwebdServer?
13 - private static var port: Int = 0
14 - private static var dataDir: String?
15 - private static var nodeUri: String?
11
12 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
18 - switch call.method {
19 - case "getPlatformVersion":
20 - result("iOS " + UIDevice.current.systemVersion)
21 - break
22 - case "start":
23 - stopServer()
24 - let args = call.arguments as? [String: String]
25 - let dataDir = args?["dataDir"]
26 - let nodeUri = args?["nodeUri"]
27 - CwMwebPlugin.dataDir = dataDir
28 - CwMwebPlugin.nodeUri = nodeUri
29 - startServer(result: result)
30 - break
31 - case "stop":
32 - stopServer()
33 - result(nil)
34 - break
35 - // case "address":
36 - // let args = call.arguments as! [String: Any]
37 - // let scanSecret = args["scanSecret"] as! FlutterStandardTypedData
38 - // let spendPub = args["spendPub"] as! FlutterStandardTypedData
39 - // let index = args["index"] as! Int32
40 -
41 - // let scanSecretData = scanSecret.data
42 - // let spendPubData = spendPub.data
43 - // result(MwebdAddress(scanSecretData, spendPubData, index))
44 - // break
45 - case "addresses":
46 - let args = call.arguments as! [String: Any]
47 - let scanSecret = args["scanSecret"] as! FlutterStandardTypedData
48 - let spendPub = args["spendPub"] as! FlutterStandardTypedData
49 - let fromIndex = args["fromIndex"] as! Int32
50 - let toIndex = args["toIndex"] as! Int32
51 -
52 - let scanSecretData = scanSecret.data
53 - let spendPubData = spendPub.data
54 - result(MwebdAddresses(scanSecretData, spendPubData, fromIndex, toIndex))
55 - break
56 - default:
57 - result(FlutterMethodNotImplemented)
58 - break
59 - }
60 - }
61 -
62 - private func startServer(result: @escaping FlutterResult) {
63 - if CwMwebPlugin.server == nil {
64 - var error: NSError?
65 - CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, CwMwebPlugin.nodeUri, &error)
66 -
67 - if let server = CwMwebPlugin.server {
68 - do {
69 - print("Starting server...")
70 - try server.start(0, ret0_: &CwMwebPlugin.port)
71 - print("Server started successfully on port: \(CwMwebPlugin.port)")
72 - result(CwMwebPlugin.port)
73 - } catch let startError as NSError {
74 - print("Server Start Error: \(startError.localizedDescription)")
75 - result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil))
76 - }
77 - } else if let error = error {
78 - print("Server Creation Error: \(error.localizedDescription)")
79 - result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil))
80 - } else {
81 - print("Unknown Error: Failed to create server")
82 - result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil))
83 - }
84 - } else {
85 - print("Server already running on port: \(CwMwebPlugin.port)")
86 - result(CwMwebPlugin.port)
87 - }
88 - }
89 -
90 - private func stopServer() {
91 - print("Stopping server")
92 - CwMwebPlugin.server?.stop()
93 - CwMwebPlugin.server = nil
94 - CwMwebPlugin.port = 0
95 - }
96 -
97 - deinit {
98 - stopServer()
13 + result(FlutterMethodNotImplemented)
14 }
15 }
cw_mweb/lib/cw_mweb.dart
+10 -13
@@ -5,9 +5,9 @@ import 'dart:io';
5 import 'dart:typed_data';
6
7 import 'package:cw_core/utils/print_verbose.dart';
8 +import 'package:cw_mweb/mweb_ffi.dart';
9 import 'package:grpc/grpc.dart';
10 import 'package:path_provider/path_provider.dart';
10 -import 'cw_mweb_platform_interface.dart';
11 import 'mwebd.pbgrpc.dart';
12
13 class CwMweb {
@@ -58,7 +58,7 @@ class CwMweb {
58 String debugLogPath = "${appDir.path}/logs/debug.log";
59 readFileWithTimer(debugLogPath);
60
61 - _port = await CwMwebPlatform.instance.start(appDir.path, nodeUriOverride ?? ltcNodeUri);
61 + _port = MWebFfi.instance.start(appDir.path, nodeUriOverride ?? ltcNodeUri);
62 if (_port == null || _port == 0) {
63 throw Exception("Failed to start server");
64 }
@@ -109,7 +109,7 @@ class CwMweb {
109
110 static Future<void> stop() async {
111 try {
112 - await CwMwebPlatform.instance.stop();
112 + MWebFfi.instance.stop();
113 await cleanup();
114 } on GrpcError catch (e) {
115 printV('Caught grpc error: ${e.message}');
@@ -118,11 +118,9 @@ class CwMweb {
118 }
119 }
120
121 - static Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async {
121 + static String? address(Uint8List scanSecret, Uint8List spendPub, int index) {
122 try {
123 - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, index, index + 1))
124 - ?.split(',')
125 - .first;
123 + return MWebFfi.instance.addresses(scanSecret, spendPub, index, index + 1).split(',').first;
124 } on GrpcError catch (e) {
125 printV('Caught grpc error: ${e.message}');
126 } catch (e) {
@@ -131,15 +129,14 @@ class CwMweb {
129 return null;
130 }
131
134 - static Future<List<String>?> addresses(
135 - Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async {
132 + static List<String>? addresses(
133 + Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) {
134 try {
137 - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, fromIndex, toIndex))
138 - ?.split(',');
135 + return MWebFfi.instance.addresses(scanSecret, spendPub, fromIndex, toIndex).split(',');
136 } on GrpcError catch (e) {
140 - log('Caught grpc error: ${e.message}');
137 + printV('Caught grpc error: ${e.message}');
138 } catch (e) {
142 - log("Error getting addresses: $e");
139 + printV("Error getting addresses: $e");
140 }
141 return null;
142 }
cw_mweb/lib/cw_mweb_method_channel.dart deleted
-58
@@ -1,58 +0,0 @@
1 -import 'dart:io' show Platform;
2 -
3 -import 'package:flutter/foundation.dart';
4 -import 'package:flutter/services.dart';
5 -
6 -import 'cw_mweb_platform_interface.dart';
7 -
8 -/// An implementation of [CwMwebPlatform] that uses method channels.
9 -class MethodChannelCwMweb extends CwMwebPlatform {
10 - /// The method channel used to interact with the native platform.
11 - @visibleForTesting
12 - final methodChannel = const MethodChannel('cw_mweb');
13 -
14 - @override
15 - Future<int?> start(String dataDir, String nodeUri) async {
16 - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
17 - return null;
18 - }
19 - final result =
20 - await methodChannel.invokeMethod<int>('start', {'dataDir': dataDir, 'nodeUri': nodeUri});
21 - return result;
22 - }
23 -
24 - @override
25 - Future<void> stop() async {
26 - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
27 - return;
28 - }
29 - await methodChannel.invokeMethod<void>('stop');
30 - }
31 -
32 - @override
33 - Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async {
34 - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
35 - return null;
36 - }
37 - final result = await methodChannel.invokeMethod<String>('address', {
38 - 'scanSecret': scanSecret,
39 - 'spendPub': spendPub,
40 - 'index': index,
41 - });
42 - return result;
43 - }
44 -
45 - @override
46 - Future<String?> addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async {
47 - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
48 - return null;
49 - }
50 - final result = await methodChannel.invokeMethod<String>('addresses', {
51 - 'scanSecret': scanSecret,
52 - 'spendPub': spendPub,
53 - 'fromIndex': fromIndex,
54 - 'toIndex': toIndex,
55 - });
56 - return result;
57 - }
58 -}
cw_mweb/lib/cw_mweb_platform_interface.dart deleted
-43
@@ -1,43 +0,0 @@
1 -import 'dart:typed_data';
2 -
3 -import 'package:plugin_platform_interface/plugin_platform_interface.dart';
4 -
5 -import 'cw_mweb_method_channel.dart';
6 -
7 -abstract class CwMwebPlatform extends PlatformInterface {
8 - /// Constructs a CwMwebPlatform.
9 - CwMwebPlatform() : super(token: _token);
10 -
11 - static final Object _token = Object();
12 -
13 - static CwMwebPlatform _instance = MethodChannelCwMweb();
14 -
15 - /// The default instance of [CwMwebPlatform] to use.
16 - ///
17 - /// Defaults to [MethodChannelCwMweb].
18 - static CwMwebPlatform get instance => _instance;
19 -
20 - /// Platform-specific implementations should set this with their own
21 - /// platform-specific class that extends [CwMwebPlatform] when
22 - /// they register themselves.
23 - static set instance(CwMwebPlatform instance) {
24 - PlatformInterface.verifyToken(instance, _token);
25 - _instance = instance;
26 - }
27 -
28 - Future<int?> start(String dataDir, String nodeUri) {
29 - throw UnimplementedError('start() has not been implemented.');
30 - }
31 -
32 - Future<void> stop() {
33 - throw UnimplementedError('stop() has not been implemented.');
34 - }
35 -
36 - Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) {
37 - throw UnimplementedError('address(int) has not been implemented.');
38 - }
39 -
40 - Future<String?> addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) {
41 - throw UnimplementedError('addresses has not been implemented.');
42 - }
43 -}
cw_mweb/lib/mweb_ffi.dart new
+68
@@ -0,0 +1,68 @@
1 +import 'dart:ffi';
2 +import 'dart:io';
3 +import 'dart:typed_data';
4 +
5 +import 'package:cw_mweb/generated_bindings.g.dart';
6 +import 'package:ffi/ffi.dart';
7 +
8 +String libPath = (() {
9 + if (Platform.isWindows) return 'mweb.dll';
10 + if (Platform.isMacOS) return 'mweb.dylib';
11 + if (Platform.isIOS) return 'Mwebd.framework/Mwebd';
12 + if (Platform.isAndroid) return 'libmweb.so';
13 + return 'libmweb.so';
14 +})();
15 +
16 +class MWebFfi {
17 + late final MWebFlutter lib;
18 +
19 + MWebFfi() : lib = MWebFlutter(DynamicLibrary.open(libPath));
20 +
21 + static MWebFfi instance = MWebFfi();
22 +
23 + int start(String dataDir, String nodeUri) {
24 + final chain = "".toNativeUtf8().cast<Char>();
25 + final dataDir_ = dataDir.toNativeUtf8().cast<Char>();
26 + final nodeUri_ = nodeUri.toNativeUtf8().cast<Char>();
27 +
28 + final port = lib.StartServer(chain, dataDir_, nodeUri_);
29 +
30 + calloc.free(chain);
31 + calloc.free(dataDir_);
32 + calloc.free(nodeUri_);
33 +
34 + return port;
35 + }
36 +
37 + void stop() => lib.StopServer();
38 +
39 + String addresses(
40 + Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) {
41 + final scanSecretPtr = malloc.allocate<Uint8>(scanSecret.length);
42 + for (int k = 0; k < scanSecret.length; k++) {
43 + scanSecretPtr[k] = scanSecret[k];
44 + }
45 +
46 + final spendPubKeyPtr = malloc.allocate<Uint8>(spendPub.length);
47 + for (int k = 0; k < spendPub.length; k++) {
48 + spendPubKeyPtr[k] = spendPub[k];
49 + }
50 +
51 + final Pointer<Char> resultPtr = lib.Addresses(
52 + scanSecretPtr.cast(),
53 + scanSecret.length,
54 + spendPubKeyPtr.cast(),
55 + spendPub.length,
56 + fromIndex,
57 + toIndex,
58 + );
59 +
60 + final result = resultPtr.cast<Utf8>().toDartString();
61 +
62 + malloc.free(scanSecretPtr);
63 + malloc.free(spendPubKeyPtr);
64 + malloc.free(resultPtr);
65 +
66 + return result;
67 + }
68 +}
cw_mweb/pubspec.yaml
+2
@@ -15,11 +15,13 @@ dependencies:
15 plugin_platform_interface: ^2.0.2
16 cw_core:
17 path: ../cw_core
18 + ffi: ^2.1.0
19
20 dev_dependencies:
21 flutter_test:
22 sdk: flutter
23 flutter_lints: ^2.0.0
24 + ffigen:
25
26 # For information on the generic Dart part of this file, see the
27 # following page: https://dart.dev/tools/pub/pubspec
ios/Podfile.lock
+2
@@ -69,6 +69,8 @@ PODS:
69 - Flutter
70 - image_picker_ios (0.0.1):
71 - Flutter
72 + - image_picker_ios (0.0.1):
73 + - Flutter
74 - in_app_review (2.0.0):
75 - Flutter
76 - integration_test (0.0.1):
ios/Runner.xcodeproj/project.pbxproj
+4
@@ -33,6 +33,7 @@
33 CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
34 D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
35 D3AD73A327249AFE8F016A51 /* BreezSDK.swift in Resources */ = {isa = PBXBuildFile; fileRef = ABD6FCBB0F4244B090459128 /* BreezSDK.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
36 + D7FC97372E4F66CF0077F194 /* Mwebd.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
37 F5EE19868D6F10D814BF73AD /* SDKNotificationService.swift in Resources */ = {isa = PBXBuildFile; fileRef = 41102141140E57B1DC27FBA1 /* SDKNotificationService.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; };
38 /* End PBXBuildFile section */
39
@@ -43,6 +44,7 @@
44 dstPath = "";
45 dstSubfolderSpec = 10;
46 files = (
47 + D7FC97372E4F66CF0077F194 /* Mwebd.xcframework in CopyFiles */,
48 CE918BF82D533ECE007F186E /* MoneroWallet.xcframework in CopyFiles */,
49 CE918BFA2D533ED4007F186E /* WowneroWallet.xcframework in CopyFiles */,
50 CE918BFC2D533ED8007F186E /* ZanoWallet.xcframework in CopyFiles */,
@@ -90,6 +92,7 @@
92 CE918BFB2D533ED8007F186E /* ZanoWallet.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = ZanoWallet.xcframework; sourceTree = "<group>"; };
93 D139E30AEB36740C21C00A9E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
94 D7CD6B6020744E8FA471915D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
95 + D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Mwebd.xcframework; path = ../cw_mweb/go/Mwebd.xcframework; sourceTree = "<group>"; };
96 DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = "<group>"; };
97 F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceLogger.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceLogger.swift"; sourceTree = "<group>"; };
98 /* End PBXFileReference section */
@@ -163,6 +166,7 @@
166 97C146E51CF9000F007C117D = {
167 isa = PBXGroup;
168 children = (
169 + D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */,
170 0C44A7182518EF4A00B570ED /* CakeWallet */,
171 9740EEB11CF90186004384FC /* Flutter */,
172 97C146F01CF9000F007C117D /* Runner */,
scripts/android/build_mwebd.sh
+38 -9
@@ -6,14 +6,43 @@ else
6 sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.24.1.linux-amd64.tar.gz
7 export PATH=$PATH:/usr/local/go/bin
8 export PATH=$PATH:~/go/bin
9 - go install golang.org/x/mobile/cmd/gomobile@latest
10 - gomobile init
9 fi
10
13 -# build mwebd:
14 -git clone https://github.com/ltcmweb/mwebd
15 -cd mwebd
16 -git reset --hard f75cb9edef07d03360ee2d33e6edae4243a890b1
17 -gomobile bind -target=android -androidapi 21 .
18 -mkdir -p ../../../cw_mweb/android/libs/
19 -cp ./mwebd.aar $_
\ No newline at end of file
11 +cd ../../cw_mweb/go
12 +
13 +if [[ "x$ANDROID_NDK_VERSION" == "x" ]];
14 +then
15 + echo "ANDROID_NDK_VERSION is missing, please declare it before building"
16 + echo "You have these versions installed on your system currently:"
17 + ls ${ANDROID_HOME}/ndk/ | cat | awk '{ print "- " $1 }'
18 + echo "echo > ~/.zprofile"
19 + echo "echo 'export ANDROID_NDK_VERSION=..... > ~/.zprofile"
20 + exit 1
21 +fi
22 +
23 +export ANDROID_OUT=../android/src/main/jniLibs
24 +export ANDROID_SDK="${HOME}/Library/Android/sdk"
25 +export NDK_BIN="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}/toolchains/llvm/prebuilt/$(uname | tr '[:upper:]' '[:lower:]')-x86_64/bin"
26 +
27 +# Compile for x86_64 architecture and place the binary file in the android/src/main/jniLibs/x86_64 folder
28 +CGO_ENABLED=1 \
29 +GOOS=android \
30 +GOARCH=amd64 \
31 +CC=${NDK_BIN}/x86_64-linux-android21-clang \
32 +go build -buildmode=c-shared -o ${ANDROID_OUT}/x86_64/libmweb.so .
33 +
34 +# Compile for arm64 architecture and place the binary file in the android/src/main/jniLibs/arm64-v8a folder
35 +CGO_ENABLED=1 \
36 +GOOS=android \
37 +GOARCH=arm64 \
38 +CC=${NDK_BIN}/aarch64-linux-android21-clang \
39 +go build -buildmode=c-shared -o ${ANDROID_OUT}/arm64-v8a/libmweb.so .
40 +
41 +# Compile for armv7a architecture and place the binary file in the android/src/main/jniLibs/armeabi-v7a folder
42 +CGO_ENABLED=1 \
43 +GOOS=android \
44 +GOARCH=arm \
45 +CC=${NDK_BIN}/armv7a-linux-androideabi21-clang \
46 +go build -buildmode=c-shared -o ${ANDROID_OUT}/armeabi-v7a/libmweb.so .
47 +cd ../
48 +dart run ffigen --config ffigen_config.yaml
scripts/ios/build_mwebd.sh
+258 -11
@@ -1,20 +1,267 @@
1 #!/bin/bash
2 +
3 +set -e -x
4 +
5 if [[ "$1" == "--dont-install" ]]; then
6 echo "Skipping Go installation as per --dont-install flag"
7 else
8 # install go > 1.24:
9 brew install go
10 export PATH=$PATH:~/go/bin
8 - go install golang.org/x/mobile/cmd/gomobile@latest
9 - gomobile init
11 fi
12
12 -# build mwebd:
13 -git clone https://github.com/ltcmweb/mwebd
14 -cd mwebd
15 -git reset --hard f75cb9edef07d03360ee2d33e6edae4243a890b1
16 -gomobile bind -target=ios .
17 -mv -fn ./Mwebd.xcframework ../../../cw_mweb/ios/
18 -# cleanup:
19 -cd ..
20 -rm -rf mwebd
\ No newline at end of file
13 +BASE_DIR="$(pwd)"
14 +MWEB_GO_DIR="${BASE_DIR}/../../cw_mweb/go"
15 +IOS_OUTPUT_DIR="${BASE_DIR}/../../cw_mweb/ios"
16 +TMP_DIR="${BASE_DIR}/tmp_mweb_frameworks"
17 +FRAMEWORK_NAME="Mwebd"
18 +
19 +rm -rf "$TMP_DIR"
20 +mkdir -p "$TMP_DIR"
21 +
22 +mkdir -p "$IOS_OUTPUT_DIR"
23 +
24 +write_info_plist() {
25 + local framework_bundle="$1"
26 + local framework_name="$2"
27 + local target="$3"
28 + local arch="$4"
29 + local plist_path="${framework_bundle}/Info.plist"
30 +
31 + local platform min_os_version dtplatformname dtsdkname
32 +
33 + if [[ "$target" == "ios-simulator" ]]; then
34 + platform="iPhoneSimulator"
35 + dtplatformname="iphonesimulator"
36 + dtsdkname="iphonesimulator17.4"
37 + min_os_version="12.0"
38 + elif [[ "$target" == "ios" ]]; then
39 + platform="iPhoneOS"
40 + dtplatformname="iphoneos"
41 + dtsdkname="iphoneos17.4"
42 + min_os_version="12.0"
43 + else
44 + echo "Unknown target: $target"
45 + exit 1
46 + fi
47 +
48 + cat > "$plist_path" <<EOF
49 +<?xml version="1.0" encoding="UTF-8"?>
50 +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
51 +<plist version="1.0">
52 +<dict>
53 + <key>BuildMachineOSBuild</key>
54 + <string>23F79</string>
55 + <key>CFBundleDevelopmentRegion</key>
56 + <string>en</string>
57 + <key>CFBundleExecutable</key>
58 + <string>${framework_name}</string>
59 + <key>CFBundleIdentifier</key>
60 + <string>com.cakewallet.${framework_name}</string>
61 + <key>CFBundleInfoDictionaryVersion</key>
62 + <string>6.0</string>
63 + <key>CFBundleName</key>
64 + <string>${framework_name}</string>
65 + <key>CFBundlePackageType</key>
66 + <string>FMWK</string>
67 + <key>CFBundleShortVersionString</key>
68 + <string>1.0</string>
69 + <key>CFBundleVersion</key>
70 + <string>1</string>
71 + <key>CFBundleSupportedPlatforms</key>
72 + <array>
73 + <string>${platform}</string>
74 + </array>
75 + <key>DTCompiler</key>
76 + <string>com.apple.compilers.llvm.clang.1_0</string>
77 + <key>DTPlatformName</key>
78 + <string>${dtplatformname}</string>
79 + <key>DTPlatformVersion</key>
80 + <string>17.4</string>
81 + <key>DTSDKBuild</key>
82 + <string>21E213</string>
83 + <key>DTSDKName</key>
84 + <string>${dtsdkname}</string>
85 + <key>DTSDKVersion</key>
86 + <string>17.4</string>
87 + <key>DTXcode</key>
88 + <string>1530</string>
89 + <key>DTXcodeBuild</key>
90 + <string>15E204a</string>
91 + <key>MinimumOSVersion</key>
92 + <string>${min_os_version}</string>
93 + <key>UIDeviceFamily</key>
94 + <array>
95 + <integer>1</integer>
96 + <integer>2</integer>
97 + </array>
98 +</dict>
99 +</plist>
100 +EOF
101 +}
102 +
103 +build_go_library() {
104 + local goos="$1"
105 + local goarch="$2"
106 + local cc_target="$3"
107 + local sdk="$4"
108 + local output_file="$5"
109 +
110 + echo "Building Go library for ${goos}/${goarch} with target ${cc_target}..."
111 +
112 + cd "$MWEB_GO_DIR"
113 +
114 + export GOOS="$goos"
115 + export GOARCH="$goarch"
116 + export CGO_ENABLED=1
117 + export CGO_CFLAGS="-fembed-bitcode"
118 +
119 + if [[ "$sdk" == "iphoneos" ]]; then
120 + export CC="$(xcrun -f clang) -target $cc_target -mios-version-min=12 --sysroot $(xcrun --sdk iphoneos --show-sdk-path)"
121 + else
122 + export CC="$(xcrun -f clang) -target $cc_target -mios-simulator-version-min=12 --sysroot $(xcrun --sdk iphonesimulator --show-sdk-path)"
123 + fi
124 +
125 + go build -buildmode=c-archive -trimpath -o "$output_file" mweb.go
126 +
127 + echo "Built: $output_file"
128 + cd - > /dev/null
129 +}
130 +
131 +create_framework() {
132 + local archive_path="$1"
133 + local framework_name="$2"
134 + local target="$3"
135 + local out_dir="$4"
136 + local arch="$5"
137 +
138 + echo "Creating ${framework_name}.framework for target ${target} (${arch}) in ${out_dir}..."
139 +
140 + local framework_bundle="${out_dir}/${framework_name}.framework"
141 +
142 + rm -rf "$framework_bundle"
143 + mkdir -p "$framework_bundle"
144 +
145 + if [[ ! -f "$archive_path" ]]; then
146 + echo "Error: Input archive not found: $archive_path"
147 + exit 1
148 + fi
149 +
150 + local temp_dir="${TMP_DIR}/temp_${arch}"
151 + mkdir -p "$temp_dir"
152 +
153 + pushd "$temp_dir"
154 + ar x "$archive_path"
155 +
156 + if [[ "$target" == "ios" ]]; then
157 + xcrun -sdk iphoneos clang -dynamiclib -arch "${arch}" -mios-version-min=12 \
158 + -install_name "@rpath/${framework_name}.framework/${framework_name}" \
159 + -framework CoreFoundation -framework Security -lresolv \
160 + -o "${framework_bundle}/${framework_name}" ./*.o
161 + else
162 + xcrun -sdk iphonesimulator clang -dynamiclib -arch "${arch}" -mios-simulator-version-min=12 \
163 + -install_name "@rpath/${framework_name}.framework/${framework_name}" \
164 + -framework CoreFoundation -framework Security -lresolv \
165 + -o "${framework_bundle}/${framework_name}" ./*.o
166 + fi
167 + popd
168 +
169 + echo "Created binary: ${framework_bundle}/${framework_name}"
170 +
171 + write_info_plist "$framework_bundle" "$framework_name" "$target" "$arch"
172 +
173 + mkdir -p "${framework_bundle}/Headers"
174 + local header_file="${archive_path%.a}.h"
175 + if [[ -f "$header_file" ]]; then
176 + cp "$header_file" "${framework_bundle}/Headers/${framework_name}.h"
177 + echo "Copied header: ${framework_bundle}/Headers/${framework_name}.h"
178 + fi
179 +
180 + echo "Framework created: ${framework_bundle}"
181 +}
182 +
183 +create_xcframework() {
184 + local framework_name="$1"
185 + local xcframework_output="$2"
186 + shift 2
187 + local frameworks=("$@")
188 +
189 + echo "Creating ${xcframework_output} by bundling:"
190 + for fw in "${frameworks[@]}"; do
191 + echo " Framework: ${fw}"
192 + done
193 +
194 + local xcodebuild_args=()
195 + for fw in "${frameworks[@]}"; do
196 + xcodebuild_args+=("-framework" "$fw")
197 + done
198 +
199 + rm -rf "$xcframework_output"
200 + xcodebuild -create-xcframework "${xcodebuild_args[@]}" -output "$xcframework_output"
201 +
202 + echo "Created XCFramework: ${xcframework_output}"
203 +}
204 +
205 +echo "Building MWEB XCFramework for iOS..."
206 +
207 +IOS_DEVICE_OUT="${TMP_DIR}/ios_device"
208 +IOS_SIMULATOR_ARM64_OUT="${TMP_DIR}/ios_simulator_arm64"
209 +IOS_SIMULATOR_X86_64_OUT="${TMP_DIR}/ios_simulator_x86_64"
210 +
211 +mkdir -p "$IOS_DEVICE_OUT" "$IOS_SIMULATOR_ARM64_OUT" "$IOS_SIMULATOR_X86_64_OUT"
212 +
213 +IOS_DEVICE_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_device.a"
214 +IOS_SIMULATOR_ARM64_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_sim_arm64.a"
215 +IOS_SIMULATOR_X86_64_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_sim_x86_64.a"
216 +
217 +build_go_library "ios" "arm64" "aarch64-apple-ios" "iphoneos" "$IOS_DEVICE_ARCHIVE"
218 +build_go_library "ios" "arm64" "aarch64-apple-ios-simulator" "iphonesimulator" "$IOS_SIMULATOR_ARM64_ARCHIVE"
219 +build_go_library "ios" "amd64" "x86_64-apple-ios-simulator" "iphonesimulator" "$IOS_SIMULATOR_X86_64_ARCHIVE"
220 +
221 +create_framework "$IOS_DEVICE_ARCHIVE" "$FRAMEWORK_NAME" "ios" "$IOS_DEVICE_OUT" "arm64"
222 +create_framework "$IOS_SIMULATOR_ARM64_ARCHIVE" "$FRAMEWORK_NAME" "ios-simulator" "$IOS_SIMULATOR_ARM64_OUT" "arm64"
223 +create_framework "$IOS_SIMULATOR_X86_64_ARCHIVE" "$FRAMEWORK_NAME" "ios-simulator" "$IOS_SIMULATOR_X86_64_OUT" "x86_64"
224 +
225 +IOS_SIMULATOR_UNIVERSAL_OUT="${TMP_DIR}/ios_simulator_universal"
226 +mkdir -p "$IOS_SIMULATOR_UNIVERSAL_OUT"
227 +
228 +SIMULATOR_UNIVERSAL_FRAMEWORK="${IOS_SIMULATOR_UNIVERSAL_OUT}/${FRAMEWORK_NAME}.framework"
229 +mkdir -p "$SIMULATOR_UNIVERSAL_FRAMEWORK"
230 +
231 +lipo -create \
232 + "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" \
233 + "${IOS_SIMULATOR_X86_64_OUT}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" \
234 + -output "${SIMULATOR_UNIVERSAL_FRAMEWORK}/${FRAMEWORK_NAME}"
235 +
236 +echo "Created universal simulator binary: ${SIMULATOR_UNIVERSAL_FRAMEWORK}/${FRAMEWORK_NAME}"
237 +
238 +cp -r "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/Info.plist" "${SIMULATOR_UNIVERSAL_FRAMEWORK}/"
239 +cp -r "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/Headers" "${SIMULATOR_UNIVERSAL_FRAMEWORK}/"
240 +
241 +IOS_DEVICE_FRAMEWORK="${IOS_DEVICE_OUT}/${FRAMEWORK_NAME}.framework"
242 +IOS_SIMULATOR_FRAMEWORK="${SIMULATOR_UNIVERSAL_FRAMEWORK}"
243 +
244 +IOS_XCFRAMEWORK="${IOS_OUTPUT_DIR}/${FRAMEWORK_NAME}.xcframework"
245 +create_xcframework "$FRAMEWORK_NAME" "$IOS_XCFRAMEWORK" "$IOS_DEVICE_FRAMEWORK" "$IOS_SIMULATOR_FRAMEWORK"
246 +
247 +echo ""
248 +echo "XCFramework created successfully:"
249 +echo " iOS XCFramework: ${IOS_XCFRAMEWORK}"
250 +echo ""
251 +
252 +FFIGEN_CONFIG="${BASE_DIR}/../../cw_mweb/ffigen_config.yaml"
253 +if [[ -f "$FFIGEN_CONFIG" ]]; then
254 + echo "Updating ffigen configuration..."
255 + sed -i.bak "s|android/src/main/jniLibs/arm64-v8a/mweb.h|ios/${FRAMEWORK_NAME}.xcframework/ios-arm64/${FRAMEWORK_NAME}.framework/Headers/${FRAMEWORK_NAME}.h|g" "$FFIGEN_CONFIG"
256 + mv "$FFIGEN_CONFIG.bak" "$FFIGEN_CONFIG"
257 + echo "Updated ffigen config to use XCFramework header"
258 +fi
259 +
260 +cd "${BASE_DIR}/../../cw_mweb"
261 +echo "Generating Dart FFI bindings..."
262 +dart run ffigen --config ffigen_config.yaml
263 +
264 +echo "Build completed successfully!"
265 +
266 +rm -rf "$TMP_DIR"
267 +echo "Temporary files cleaned up."
\ No newline at end of file