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
15 changes: 15 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@
<data android:mimeType="audio/*"/>
<data android:mimeType="application/*"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="erebrusdrop" android:host="auth"/>
</intent-filter>
</activity>
<activity
android:name=".NativeQrScannerActivity"
Expand Down Expand Up @@ -118,5 +124,14 @@
<intent>
<action android:name="solana.mobilewalletadapter.action.AUTHORIZE"/>
</intent>
<intent>
<action android:name="solana.mobilewalletadapter.action.SIGN_MESSAGE"/>
</intent>
<package android:name="com.solanamobile.seedvaultimpl"/>
<package android:name="app.phantom"/>
<package android:name="com.solflare.mobile"/>
<package android:name="io.metamask"/>
<package android:name="com.wallet.crypto.trustapp"/>
<package android:name="me.rainbow"/>
</queries>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ class MainActivity : FlutterActivity() {
result = result,
)
}
"signMessage" -> {
val packageName = call.argument<String>("packageName")
val authToken = call.argument<String>("authToken")
val message = call.argument<String>("message")
if (packageName.isNullOrBlank() || authToken.isNullOrBlank() || message.isNullOrBlank()) {
result.error("INVALID_ARGS", "packageName, authToken and message are required.", null)
return@setMethodCallHandler
}
SolanaWalletBridge.signMessage(
activity = this,
packageName = packageName,
authToken = authToken,
message = message,
result = result,
)
}
else -> result.notImplemented()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,101 @@ object SolanaWalletBridge {
}
}

fun signMessage(
activity: Activity,
packageName: String,
authToken: String,
message: String,
result: MethodChannel.Result,
) {
if (pendingResult != null) {
result.error("BUSY", "Wallet connection already in progress.", null)
return
}
if (packageName.isBlank() || authToken.isBlank() || message.isBlank()) {
result.error("INVALID_ARGS", "packageName, authToken and message are required.", null)
return
}

pendingResult = result
val scenario = LocalAssociationScenario(Scenario.DEFAULT_CLIENT_TIMEOUT_MS)
pendingScenario = scenario
val packageManager = activity.packageManager
val wallet = knownWalletFor(packageName)
val associationIntent = if (wallet != null && canHandleMwaAssociation(packageManager, wallet)) {
buildAssociationIntent(packageManager, wallet, scenario)
} else {
null
}

timeoutRunnable = Runnable {
failPending("Wallet sign-in timed out after 30 seconds. Try again.")
}
mainHandler.postDelayed(timeoutRunnable!!, CONNECT_TIMEOUT_MS)

executor.execute {
try {
if (associationIntent != null) {
val launchLatch = java.util.concurrent.CountDownLatch(1)
var launchError: Throwable? = null
mainHandler.post {
try {
activity.startActivityForResult(associationIntent, WALLET_REQUEST_CODE)
} catch (error: Throwable) {
launchError = error
} finally {
launchLatch.countDown()
}
}
launchLatch.await()
if (launchError != null) {
throw launchError!!
}
}

val client = scenario.start().get()
val authResult = client.reauthorize(
MWA_IDENTITY_URI,
MWA_ICON_RELATIVE_URI,
"Erebrus Drop",
authToken,
).get()

val messageBytes = message.toByteArray(Charsets.UTF_8)
val signed = client.signMessages(
listOf(authResult.publicKey),
listOf(messageBytes),
).get()

if (signed.signedMessages.isEmpty() || signed.signedMessages[0].signatures.isEmpty()) {
throw IllegalStateException("Wallet did not return a signature.")
}

val signatureBytes = signed.signedMessages[0].signatures[0]
val signatureHex = signatureBytes.joinToString("") { "%02x".format(it) }

clearTimeout()
mainHandler.post {
completePending(
mapOf(
"publicKey" to authResult.publicKey,
"authToken" to authResult.authToken,
"signature" to signatureHex,
),
)
scenario.close()
pendingScenario = null
}
} catch (error: Throwable) {
mainHandler.post {
failPending(error.message ?: "Wallet sign-in failed.")
scenario.close()
pendingScenario = null
}
}
}
}

fun deauthorizeWallet(
activity: Activity,
packageName: String,
Expand Down
11 changes: 11 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,16 @@
<true/>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.erebrus.drop.auth</string>
<key>CFBundleURLSchemes</key>
<array>
<string>erebrusdrop</string>
</array>
</dict>
</array>
</dict>
</plist>
Loading
Loading