diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index d5a88ba..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-version: 2
-jobs:
- build:
- working_directory: ~/code
- docker:
- - image: cimg/android:2021.10.2-node
- environment:
- JVM_OPTS: -Xmx3200m
- steps:
- - checkout
- - run:
- name: Generate google-services.json
- command: echo $GOOGLE_SERVICES | base64 --decode > app/google-services.json
- - run:
- name: Download Dependencies
- command: ./gradlew androidDependencies
- - run:
- name: Assemble
- command: ./gradlew assemble
- - run:
- name: Unit Tests
- command: ./gradlew lint test
- - store_artifacts:
- path: core/build/reports
- destination: core/reports
- - store_artifacts:
- path: solana/build/reports
- destination: solana/reports
- - store_test_results:
- path: core/build/test-results
- - store_test_results:
- path: solana/build/test-results
-
-workflows:
- version: 2
- workflow:
- jobs:
- - build
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
new file mode 100644
index 0000000..5f11316
--- /dev/null
+++ b/.github/actions/setup/action.yml
@@ -0,0 +1,31 @@
+name: Setup
+
+description: Setup
+
+inputs:
+ google_services:
+ description: 'Google Services file'
+ required: true
+
+runs:
+ using: "composite"
+ steps:
+ - uses: actions/setup-java@v3
+ with:
+ distribution: 'zulu'
+ java-version: 17
+ - uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
+
+ - name: Check Gradle wrapper
+ uses: gradle/wrapper-validation-action@v1
+
+ - name: Put Google Services file
+ env:
+ GOOGLE_SERVICES: ${{ inputs.google_services }}
+ run: echo "$GOOGLE_SERVICES" | base64 -d > app/google-services.json
+ shell: bash
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..e8ecb4c
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,8 @@
+## Ticket
+
+
+## Changes
+
+
+## Screenshots
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..3a8a9e1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,140 @@
+name: CI
+
+on: push
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup
+ uses: ./.github/actions/setup
+ with:
+ google_services: ${{ secrets.GOOGLE_SERVICES }}
+
+ - name: Run lint
+ run: ./gradlew lint
+
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup
+ uses: ./.github/actions/setup
+ with:
+ google_services: ${{ secrets.GOOGLE_SERVICES }}
+
+ - name: Run test
+ run: ./gradlew test
+
+ build:
+ name: Build
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ needs: [ lint, test ]
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup
+ uses: ./.github/actions/setup
+ with:
+ google_services: ${{ secrets.GOOGLE_SERVICES }}
+
+ - name: Build release
+ run: ./gradlew clean assembleRelease --stacktrace
+
+ distribute:
+ if: startsWith(github.ref_name, 'release/')
+ name: Distribute
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ needs: build
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup
+ uses: ./.github/actions/setup
+ with:
+ google_services: ${{ secrets.GOOGLE_SERVICES }}
+
+ - name: Put files
+ env:
+ KEYSTORE: ${{ secrets.KEYSTORE }}
+ SIGNING: ${{ secrets.SIGNING }}
+ APP_DISTRIBUTION: ${{ secrets.APP_DISTRIBUTION }}
+ run: |
+ echo "Adding files"
+ TMP_SECRETS_PATH=secrets
+ mkdir ${TMP_SECRETS_PATH}
+ echo $KEYSTORE | base64 -d > "${TMP_SECRETS_PATH}"/portto.jjs
+ echo $SIGNING | base64 -d > "${TMP_SECRETS_PATH}"/signing.properties
+ echo $APP_DISTRIBUTION | base64 -d > "${TMP_SECRETS_PATH}"/app-distribution.json
+ echo "All files Added ✅"
+
+ - name: Fill Infura id
+ run: sed -i 's/INFURA_ID.*/INFURA_ID = \"${{ secrets.INFURA_ID }}\"/' app/src/main/java/com/portto/valuedapp/Config.kt
+
+ - name: Distribute sample app
+ run: bash ./scripts/distribute.sh
+
+ publish:
+ if: github.ref_name == 'main'
+ name: Publish
+ runs-on: ubuntu-latest
+ timeout-minutes: 10
+ needs: build
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Setup
+ uses: ./.github/actions/setup
+ with:
+ google_services: ${{ secrets.GOOGLE_SERVICES }}
+
+ - name: Publish library
+ env:
+ ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }}
+ ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_PASSWORD }}
+ ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_NEXUS_USERNAME }}
+ ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_NEXUS_PASSWORD }}
+ run: bash ./scripts/publish.sh
+
+ - name: Get version
+ run: |
+ echo "version=$(grep "versionName" dependencies.gradle | awk -F: '{print $2}' | tr -d ' '\',)" >> $GITHUB_ENV
+
+ - name: Create and push tag
+ run: |
+ git config user.name "GitHub Actions"
+ git config user.email noreply@github.com
+ git tag -a ${{ env.version }} -m "Release v${{ env.version }}"
+ git push origin ${{ env.version }}
+
+ - name: Create release
+ uses: softprops/action-gh-release@v1
+ with:
+ name: v${{ env.version }}
+ tag_name: ${{ env.version }}
+ draft: false
+ prerelease: false
+
+ - name: Create pull request (main -> develop)
+ run: >
+ gh pr create
+ --base develop
+ --head main
+ --title '[${{ env.version }}] Merge main into develop'
+ --body 'Created by Github Actions'
+ --reviewer Doge-is-Dope,kihonyoo,imjacklai
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/create-asana-attachment.yml b/.github/workflows/create-asana-attachment.yml
new file mode 100644
index 0000000..3913a20
--- /dev/null
+++ b/.github/workflows/create-asana-attachment.yml
@@ -0,0 +1,17 @@
+name: Asana
+on:
+ pull_request:
+ types: [opened, reopened]
+
+jobs:
+ create-asana-attachment-job:
+ runs-on: ubuntu-latest
+ name: Create pull request attachments on Asana tasks
+ steps:
+ - name: Create pull request attachments
+ uses: Asana/create-app-attachment-github-action@latest
+ id: postAttachment
+ with:
+ asana-secret: ${{ secrets.ASANA_SECRET }}
+ - name: Log output status
+ run: echo "Status is ${{ steps.postAttachment.outputs.status }}"
\ No newline at end of file
diff --git a/README.md b/README.md
index 8a0daa2..2856ea9 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,20 @@
# BloctoSDK
![Maven Central](https://img.shields.io/maven-central/v/com.portto.sdk/core)
-![CircleCI](https://img.shields.io/circleci/build/gh/portto/blocto-android-sdk/main)
+![Github Action](https://github.com/portto/blocto-android-sdk/actions/workflows/ci.yml/badge.svg)
![GitHub](https://img.shields.io/github/license/portto/blocto-android-sdk)
Integrate Blocto service into your dApp on Android.
Currently support
-* Solana
* Ethereum
+* Arbitrum
+* Optimism
* BNB Chain
* Polygon
* Avalanche
+* Solana
* More blockchains are coming soon
> For Flow, it's recommended to use [fcl](https://github.com/portto/fcl-android). Check the [documents](https://docs.blocto.app/blocto-sdk/android-sdk/flow) for more info.
diff --git a/app/build.gradle b/app/build.gradle
index 7c170b9..91efeab 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,6 +18,7 @@ android {
targetSdk versions.compileSdk
versionCode 1
versionName "1.0.0"
+ versionNameSuffix "-" + getDate()
}
def signingFilePath = "secrets/signing.properties"
@@ -67,6 +68,10 @@ android {
}
}
+static def getDate() {
+ return new Date().format('yyyyMMddHHmm')
+}
+
dependencies {
implementation project(':evm')
implementation project(':solana')
@@ -92,9 +97,9 @@ dependencies {
exclude module: 'bcprov-jdk15on'
}
- implementation("com.nftco:flow-jvm-sdk:0.7.1") {
+ implementation("com.nftco:flow-jvm-sdk:0.7.3") {
exclude module: 'bcprov-jdk15on'
}
- implementation("io.grpc:grpc-okhttp:1.47.0")
+ implementation("io.grpc:grpc-okhttp:1.50.2")
implementation platform("com.google.firebase:firebase-bom:29.3.1")
}
diff --git a/app/src/main/java/com/portto/valuedapp/Config.kt b/app/src/main/java/com/portto/valuedapp/Config.kt
index b940d44..99da5f3 100644
--- a/app/src/main/java/com/portto/valuedapp/Config.kt
+++ b/app/src/main/java/com/portto/valuedapp/Config.kt
@@ -4,8 +4,8 @@ object Config {
const val INFURA_ID = ""
// Your Blocto App ID
- const val APP_ID_MAINNET = "49618b1e-461b-4134-8f52-4307dd542a88"
- const val APP_ID_TESTNET = "57f397df-263c-4e97-b61f-15b67b9ce285"
+ const val APP_ID_MAINNET = "0896e44c-20fd-443b-b664-d305b52fe8e8"
+ const val APP_ID_TESTNET = "0896e44c-20fd-443b-b664-d305b52fe8e8"
// Required by Flow
const val FLOW_APP_IDENTIFIER = "Awesome App (v0.0)"
diff --git a/app/src/main/java/com/portto/valuedapp/evm/EvmChain.kt b/app/src/main/java/com/portto/valuedapp/evm/EvmChain.kt
index 260954f..ce0d6f3 100644
--- a/app/src/main/java/com/portto/valuedapp/evm/EvmChain.kt
+++ b/app/src/main/java/com/portto/valuedapp/evm/EvmChain.kt
@@ -1,10 +1,14 @@
package com.portto.valuedapp.evm
+import com.portto.sdk.core.Blockchain
import com.portto.valuedapp.Config
enum class EvmChain(
+ val blockchain: Blockchain,
val title: String,
val symbol: String,
+ val mainnetChainId: Int,
+ val testnetChainId: Int,
val mainnetContractAddress: String,
val testnetContractAddress: String,
val mainnetRpcUrl: String,
@@ -14,43 +18,81 @@ enum class EvmChain(
) {
ETHEREUM(
+ blockchain = Blockchain.ETHEREUM,
title = "Ethereum",
symbol = "ETH",
- mainnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
- testnetContractAddress = "0x58F385777aa6699b81f741Dd0d5B272A34C1c774",
+ mainnetChainId = 1,
+ testnetChainId = 5,
+ mainnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
+ testnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
mainnetRpcUrl = "https://mainnet.infura.io/v3/${Config.INFURA_ID}",
- testnetRpcUrl = "https://rinkeby.blocto.app",
+ testnetRpcUrl = "https://goerli.infura.io/v3/${Config.INFURA_ID}",
mainnetExplorerDomain = "etherscan.io",
- testnetExplorerDomain = "rinkeby.etherscan.io"
+ testnetExplorerDomain = "goerli.etherscan.io"
),
BNB_CHAIN(
+ blockchain = Blockchain.BNB_CHAIN,
title = "BNB Chain",
symbol = "BNB",
- mainnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
- testnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
+ mainnetChainId = 56,
+ testnetChainId = 97,
+ mainnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
+ testnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
mainnetRpcUrl = "https://bsc-dataseed.binance.org",
testnetRpcUrl = "https://data-seed-prebsc-1-s1.binance.org:8545",
mainnetExplorerDomain = "bscscan.com",
testnetExplorerDomain = "testnet.bscscan.com"
),
POLYGON(
+ blockchain = Blockchain.POLYGON,
title = "Polygon",
symbol = "MATIC",
- mainnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
- testnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
+ mainnetChainId = 137,
+ testnetChainId = 80001,
+ mainnetContractAddress = "0xD76bAA840e3D5AE1C5E5C7cEeF1C1A238687860e",
+ testnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
mainnetRpcUrl = "https://polygon-mainnet.infura.io/v3/${Config.INFURA_ID}",
testnetRpcUrl = "https://polygon-mumbai.infura.io/v3/${Config.INFURA_ID}",
mainnetExplorerDomain = "polygonscan.com",
testnetExplorerDomain = "mumbai.polygonscan.com"
),
AVALANCHE(
+ blockchain = Blockchain.AVALANCHE,
title = "Avalanche",
symbol = "AVAX",
- mainnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
- testnetContractAddress = "0xfde90c9Bc193F520d119302a2dB8520D3A4408c8",
+ mainnetChainId = 43114,
+ testnetChainId = 43113,
+ mainnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
+ testnetContractAddress = "0xD76bAA840e3D5AE1C5E5C7cEeF1C1A238687860e",
mainnetRpcUrl = "https://api.avax.network/ext/bc/C/rpc",
testnetRpcUrl = "https://api.avax-test.network/ext/bc/C/rpc",
mainnetExplorerDomain = "snowtrace.io",
testnetExplorerDomain = "testnet.snowtrace.io"
- )
+ ),
+ ARBITRUM(
+ blockchain = Blockchain.ARBITRUM,
+ title = "Arbitrum",
+ symbol = "ETH",
+ mainnetChainId = 42161,
+ testnetChainId = 421613,
+ mainnetContractAddress = "0x806243c7368a90D957592B55875eF4C3353C5bEa",
+ testnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
+ mainnetRpcUrl = "https://arb1.arbitrum.io/rpc",
+ testnetRpcUrl = "https://arbitrum-goerli.public.blastapi.io",
+ mainnetExplorerDomain = "arbiscan.io",
+ testnetExplorerDomain = "goerli.arbiscan.io"
+ ),
+ OPTIMISM(
+ blockchain = Blockchain.OPTIMISM,
+ title = "Optimism",
+ symbol = "ETH",
+ mainnetChainId = 10,
+ testnetChainId = 420,
+ mainnetContractAddress = "0x806243c7368a90D957592B55875eF4C3353C5bEa",
+ testnetContractAddress = "0x009c403BdFaE357d82AAef2262a163287c30B739",
+ mainnetRpcUrl = "https://mainnet.optimism.io",
+ testnetRpcUrl = "https://endpoints.omniatech.io/v1/op/goerli/public",
+ mainnetExplorerDomain = "optimistic.etherscan.io",
+ testnetExplorerDomain = "goerli-optimism.etherscan.io"
+ ),
}
diff --git a/app/src/main/java/com/portto/valuedapp/evm/EvmSendTransactionFragment.kt b/app/src/main/java/com/portto/valuedapp/evm/EvmSendTransactionFragment.kt
index 15f2a0c..7ecc552 100644
--- a/app/src/main/java/com/portto/valuedapp/evm/EvmSendTransactionFragment.kt
+++ b/app/src/main/java/com/portto/valuedapp/evm/EvmSendTransactionFragment.kt
@@ -9,10 +9,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.portto.sdk.core.BloctoSDK
-import com.portto.sdk.evm.avalanche
-import com.portto.sdk.evm.bnb
-import com.portto.sdk.evm.ethereum
-import com.portto.sdk.evm.polygon
+import com.portto.sdk.evm.evm
import com.portto.sdk.wallet.BloctoEnv
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.valuedapp.R
@@ -206,44 +203,16 @@ class EvmSendTransactionFragment : Fragment(R.layout.fragment_evm_send_transacti
onSuccess: (String) -> Unit,
onError: (BloctoSDKError) -> Unit
) {
- when (viewModel.currentChain) {
- EvmChain.ETHEREUM -> BloctoSDK.ethereum.sendTransaction(
- context = requireContext(),
- fromAddress = fromAddress,
- toAddress = contractAddress,
- data = data,
- value = value,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.BNB_CHAIN -> BloctoSDK.bnb.sendTransaction(
- context = requireContext(),
- fromAddress = fromAddress,
- toAddress = contractAddress,
- data = data,
- value = value,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.POLYGON -> BloctoSDK.polygon.sendTransaction(
- context = requireContext(),
- fromAddress = fromAddress,
- toAddress = contractAddress,
- data = data,
- value = value,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.AVALANCHE -> BloctoSDK.avalanche.sendTransaction(
- context = requireContext(),
- fromAddress = fromAddress,
- toAddress = contractAddress,
- data = data,
- value = value,
- onSuccess = onSuccess,
- onError = onError
- )
- }
+ BloctoSDK.evm.sendTransaction(
+ context = requireContext(),
+ blockchain = viewModel.currentChain.blockchain,
+ fromAddress = fromAddress,
+ toAddress = contractAddress,
+ data = data,
+ value = value,
+ onSuccess = onSuccess,
+ onError = onError
+ )
}
private fun openExplorer(txHash: String) {
diff --git a/app/src/main/java/com/portto/valuedapp/evm/EvmSignMessageFragment.kt b/app/src/main/java/com/portto/valuedapp/evm/EvmSignMessageFragment.kt
index bbf5105..d489712 100644
--- a/app/src/main/java/com/portto/valuedapp/evm/EvmSignMessageFragment.kt
+++ b/app/src/main/java/com/portto/valuedapp/evm/EvmSignMessageFragment.kt
@@ -9,10 +9,7 @@ import androidx.lifecycle.lifecycleScope
import com.portto.ethereum.sign.EthSigUtil
import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.decodeHex
-import com.portto.sdk.evm.avalanche
-import com.portto.sdk.evm.bnb
-import com.portto.sdk.evm.ethereum
-import com.portto.sdk.evm.polygon
+import com.portto.sdk.evm.evm
import com.portto.sdk.wallet.BloctoEnv
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.evm.EvmSignType
@@ -47,41 +44,24 @@ class EvmSignMessageFragment : Fragment(R.layout.fragment_evm_sign_message) {
private lateinit var binding: FragmentEvmSignMessageBinding
private val viewModel: EvmViewModel by activityViewModels()
+ private var signType = EvmSignType.ETH_SIGN
+
private val rpcUrl get() = when (BloctoSDK.env) {
BloctoEnv.PROD -> viewModel.currentChain.mainnetRpcUrl
BloctoEnv.DEV -> viewModel.currentChain.testnetRpcUrl
}
+ private val chainId get() = when (BloctoSDK.env) {
+ BloctoEnv.PROD -> viewModel.currentChain.mainnetChainId
+ BloctoEnv.DEV -> viewModel.currentChain.testnetChainId
+ }
+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentEvmSignMessageBinding.bind(view)
- var signType = EvmSignType.ETH_SIGN
-
- binding.chipGroup.setOnCheckedStateChangeListener { _, checkedIds ->
- val checkedId = checkedIds.firstOrNull() ?: return@setOnCheckedStateChangeListener
- when (checkedId) {
- binding.ethSign.id -> {
- signType = EvmSignType.ETH_SIGN
- binding.input.setText("0x416e79206d65737361676520796f752077616e6e61207369676e")
- }
- binding.personalSign.id -> {
- signType = EvmSignType.PERSONAL_SIGN
- binding.input.setText("Any message you wanna sign")
- }
- binding.typedDataV3.id -> {
- signType = EvmSignType.TYPED_DATA_SIGN_V3
- binding.input.setText(getString(R.string.default_typed_data_v3))
- }
- binding.typedDataV4.id -> {
- signType = EvmSignType.TYPED_DATA_SIGN_V4
- binding.input.setText(getString(R.string.default_typed_data_v4))
- }
- binding.typedData.id -> {
- signType = EvmSignType.TYPED_DATA_SIGN
- binding.input.setText(getString(R.string.default_typed_data_v4))
- }
- }
+ binding.chipGroup.setOnCheckedStateChangeListener { _, _ ->
+ setupMessage()
resetView()
}
@@ -91,6 +71,7 @@ class EvmSignMessageFragment : Fragment(R.layout.fragment_evm_sign_message) {
}
viewModel.resetView.observe(viewLifecycleOwner) {
+ setupMessage()
resetView()
}
}
@@ -100,6 +81,32 @@ class EvmSignMessageFragment : Fragment(R.layout.fragment_evm_sign_message) {
view?.clearFocus()
}
+ private fun setupMessage() {
+ val checkedId = binding.chipGroup.checkedChipIds.firstOrNull() ?: return
+ when (checkedId) {
+ binding.ethSign.id -> {
+ signType = EvmSignType.ETH_SIGN
+ binding.input.setText("0x416e79206d65737361676520796f752077616e6e61207369676e")
+ }
+ binding.personalSign.id -> {
+ signType = EvmSignType.PERSONAL_SIGN
+ binding.input.setText("Any message you wanna sign")
+ }
+ binding.typedDataV3.id -> {
+ signType = EvmSignType.TYPED_DATA_SIGN_V3
+ binding.input.setText(getString(R.string.default_typed_data_v3, chainId))
+ }
+ binding.typedDataV4.id -> {
+ signType = EvmSignType.TYPED_DATA_SIGN_V4
+ binding.input.setText(getString(R.string.default_typed_data_v4, chainId))
+ }
+ binding.typedData.id -> {
+ signType = EvmSignType.TYPED_DATA_SIGN
+ binding.input.setText(getString(R.string.default_typed_data_v4, chainId))
+ }
+ }
+ }
+
private fun signMessage(signType: EvmSignType) {
resetView()
@@ -153,40 +160,15 @@ class EvmSignMessageFragment : Fragment(R.layout.fragment_evm_sign_message) {
viewModel.showError(it)
}
- when (viewModel.currentChain) {
- EvmChain.ETHEREUM -> BloctoSDK.ethereum.signMessage(
- context = requireContext(),
- fromAddress = address,
- signType = signType,
- message = message,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.BNB_CHAIN -> BloctoSDK.bnb.signMessage(
- context = requireContext(),
- fromAddress = address,
- signType = signType,
- message = message,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.POLYGON -> BloctoSDK.polygon.signMessage(
- context = requireContext(),
- fromAddress = address,
- signType = signType,
- message = message,
- onSuccess = onSuccess,
- onError = onError
- )
- EvmChain.AVALANCHE -> BloctoSDK.avalanche.signMessage(
- context = requireContext(),
- fromAddress = address,
- signType = signType,
- message = message,
- onSuccess = onSuccess,
- onError = onError
- )
- }
+ BloctoSDK.evm.signMessage(
+ context = requireContext(),
+ blockchain = viewModel.currentChain.blockchain,
+ fromAddress = address,
+ signType = signType,
+ message = message,
+ onSuccess = onSuccess,
+ onError = onError
+ )
}
private fun verifySignature(hash: ByteArray, signature: ByteArray): Boolean {
diff --git a/app/src/main/java/com/portto/valuedapp/evm/EvmValueDappActivity.kt b/app/src/main/java/com/portto/valuedapp/evm/EvmValueDappActivity.kt
index 82b1647..94e81c1 100644
--- a/app/src/main/java/com/portto/valuedapp/evm/EvmValueDappActivity.kt
+++ b/app/src/main/java/com/portto/valuedapp/evm/EvmValueDappActivity.kt
@@ -8,10 +8,7 @@ import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.portto.sdk.core.BloctoSDK
-import com.portto.sdk.evm.avalanche
-import com.portto.sdk.evm.bnb
-import com.portto.sdk.evm.ethereum
-import com.portto.sdk.evm.polygon
+import com.portto.sdk.evm.evm
import com.portto.sdk.wallet.BloctoEnv
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.valuedapp.Config.APP_ID_MAINNET
@@ -134,27 +131,11 @@ class EvmValueDappActivity : AppCompatActivity() {
viewModel.showError(it)
}
- when (viewModel.currentChain) {
- EvmChain.ETHEREUM -> BloctoSDK.ethereum.requestAccount(
- context = this,
- onSuccess = requestAccountOnSuccess,
- onError = requestAccountOnError
- )
- EvmChain.BNB_CHAIN -> BloctoSDK.bnb.requestAccount(
- context = this,
- onSuccess = requestAccountOnSuccess,
- onError = requestAccountOnError
- )
- EvmChain.POLYGON -> BloctoSDK.polygon.requestAccount(
- context = this,
- onSuccess = requestAccountOnSuccess,
- onError = requestAccountOnError
- )
- EvmChain.AVALANCHE -> BloctoSDK.avalanche.requestAccount(
- context = this,
- onSuccess = requestAccountOnSuccess,
- onError = requestAccountOnError
- )
- }
+ BloctoSDK.evm.requestAccount(
+ context = this,
+ blockchain = viewModel.currentChain.blockchain,
+ onSuccess = requestAccountOnSuccess,
+ onError = requestAccountOnError
+ )
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ea4d49c..157dfe1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -18,8 +18,8 @@
Typed Data
Composite Signatures
Sign
- {\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":4,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}
- {\"domain\":{\"chainId\":4,\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}]},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}
+ {\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallet\",\"type\":\"address\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person\"},{\"name\":\"contents\",\"type\":\"string\"}]},\"primaryType\":\"Mail\",\"domain\":{\"name\":\"Ether Mail\",\"version\":\"1\",\"chainId\":%1$d,\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\"},\"message\":{\"from\":{\"name\":\"Cow\",\"wallet\":\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\"},\"to\":{\"name\":\"Bob\",\"wallet\":\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\"},\"contents\":\"Hello, Bob!\"}}
+ {\"domain\":{\"chainId\":%1$d,\"name\":\"Ether Mail\",\"verifyingContract\":\"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC\",\"version\":\"1\"},\"message\":{\"contents\":\"Hello, Bob!\",\"from\":{\"name\":\"Cow\",\"wallets\":[\"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826\",\"0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF\"]},\"to\":[{\"name\":\"Bob\",\"wallets\":[\"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB\",\"0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57\",\"0xB0B0b0b0b0b0B000000000000000000000000000\"]}]},\"primaryType\":\"Mail\",\"types\":{\"EIP712Domain\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"version\",\"type\":\"string\"},{\"name\":\"chainId\",\"type\":\"uint256\"},{\"name\":\"verifyingContract\",\"type\":\"address\"}],\"Group\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"members\",\"type\":\"Person[]\"}],\"Mail\":[{\"name\":\"from\",\"type\":\"Person\"},{\"name\":\"to\",\"type\":\"Person[]\"},{\"name\":\"contents\",\"type\":\"string\"}],\"Person\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"wallets\",\"type\":\"address[]\"}]}}
Show signature
Signature: %1$s
isAuthorizedSigner: %1$s
diff --git a/build.gradle b/build.gradle
index a6b8700..80cd6a6 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,15 +1,15 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
- classpath "com.vanniktech:gradle-maven-publish-plugin:0.20.0"
- classpath "com.google.gms:google-services:4.3.14"
- classpath "com.google.firebase:firebase-appdistribution-gradle:3.0.3"
+ classpath "com.vanniktech:gradle-maven-publish-plugin:0.22.0"
+ classpath "com.google.gms:google-services:4.3.15"
+ classpath "com.google.firebase:firebase-appdistribution-gradle:4.0.0"
}
}
plugins {
- id 'com.android.application' version '7.3.0' apply false
- id 'com.android.library' version '7.3.0' apply false
- id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
+ id 'com.android.application' version '8.0.1' apply false
+ id 'com.android.library' version '8.0.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.8.21' apply false
id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.21'
}
task clean(type: Delete) {
diff --git a/core/build.gradle b/core/build.gradle
index cea44a5..e602cca 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -49,7 +49,7 @@ dependencies {
implementation "com.google.android.material:material:$versions.material"
implementation "com.squareup.okhttp3:okhttp:$versions.okhttp"
implementation "com.squareup.okhttp3:logging-interceptor:$versions.okhttp"
- implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$versions.serialization"
+ api "org.jetbrains.kotlinx:kotlinx-serialization-json:$versions.serialization"
testImplementation "junit:junit:$versions.junit"
testImplementation "io.mockk:mockk:$versions.mockk"
diff --git a/core/src/main/java/com/portto/sdk/core/Blockchain.kt b/core/src/main/java/com/portto/sdk/core/Blockchain.kt
index 71c0251..efc48f0 100644
--- a/core/src/main/java/com/portto/sdk/core/Blockchain.kt
+++ b/core/src/main/java/com/portto/sdk/core/Blockchain.kt
@@ -6,5 +6,7 @@ enum class Blockchain(val value: String) {
BNB_CHAIN("bsc"),
POLYGON("polygon"),
AVALANCHE("avalanche"),
+ ARBITRUM("arbitrum"),
+ OPTIMISM("optimism"),
FLOW("flow"),
}
diff --git a/core/src/main/java/com/portto/sdk/core/BloctoApi.kt b/core/src/main/java/com/portto/sdk/core/BloctoApi.kt
index 74f09ac..b6032ad 100644
--- a/core/src/main/java/com/portto/sdk/core/BloctoApi.kt
+++ b/core/src/main/java/com/portto/sdk/core/BloctoApi.kt
@@ -1,12 +1,13 @@
package com.portto.sdk.core
import androidx.annotation.WorkerThread
-import androidx.viewbinding.BuildConfig
+import com.portto.sdk.core.BloctoApi.toErrorCode
import com.portto.sdk.core.BuildConfig.VERSION_NAME
-import com.portto.sdk.wallet.BloctoEnv
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.jsonObject
+import kotlinx.serialization.json.jsonPrimitive
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
@@ -21,12 +22,6 @@ internal object BloctoApi {
val jsonType = "application/json; charset=utf-8".toMediaType()
- val baseUrl
- get() = when (BloctoSDK.env) {
- BloctoEnv.PROD -> "https://api.blocto.app"
- BloctoEnv.DEV -> "https://api-dev.blocto.app/"
- }
-
private val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
@@ -54,11 +49,17 @@ internal object BloctoApi {
.addInterceptor(headerInterceptor)
.addInterceptor(loggingInterceptor)
.build()
+
+ fun String.toErrorCode() = json.parseToJsonElement(this)
+ .jsonObject["error_code"]
+ ?.jsonPrimitive
+ ?.content
+ ?: this
}
-inline fun get(path: String): T {
+inline fun get(url: String): T {
val request = Request.Builder()
- .url("${BloctoApi.baseUrl}/$path")
+ .url(url)
.get()
.build()
@@ -66,24 +67,33 @@ inline fun get(path: String): T {
if (it.isSuccessful) {
return BloctoApi.json.decodeFromString(it.body?.string().orEmpty())
} else {
- throw Exception("code: ${it.code}, message=${it.body?.string()}")
+ throw Exception(it.body?.string()?.toErrorCode())
}
}
}
-inline fun post(path: String, requestBody: U): T {
+inline fun post(
+ url: String,
+ requestBody: U,
+ headers: Map? = null
+): T {
val body = BloctoApi.json.encodeToString(requestBody).toRequestBody(BloctoApi.jsonType)
val request = Request.Builder()
- .url("${BloctoApi.baseUrl}/$path")
+ .url(url)
.post(body)
+ .apply {
+ for ((key, value) in (headers ?: emptyMap())) {
+ addHeader(key, value)
+ }
+ }
.build()
BloctoApi.client.newCall(request).execute().use {
if (it.isSuccessful) {
return BloctoApi.json.decodeFromString(it.body?.string().orEmpty())
} else {
- throw Exception("code: ${it.code}, message=${it.body?.string()}")
+ throw Exception(it.body?.string()?.toErrorCode())
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/main/java/com/portto/sdk/core/BloctoSDK.kt b/core/src/main/java/com/portto/sdk/core/BloctoSDK.kt
index d93b1a8..f2c079d 100644
--- a/core/src/main/java/com/portto/sdk/core/BloctoSDK.kt
+++ b/core/src/main/java/com/portto/sdk/core/BloctoSDK.kt
@@ -7,14 +7,21 @@ import android.net.Uri
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.portto.sdk.core.method.Method
+import com.portto.sdk.core.method.RequestAccountMethod
import com.portto.sdk.wallet.BloctoEnv
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.Const
+import kotlinx.coroutines.CoroutineExceptionHandler
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.util.*
object BloctoSDK {
private var appId: String? = null
+ private var webSessionId: String? = null
private val requestMap = mutableMapOf>()
@JvmStatic
@@ -47,14 +54,33 @@ object BloctoSDK {
} catch (e: ActivityNotFoundException) {
if (method.blockchain == Blockchain.FLOW)
Log.w("BloctoSDK", "Flow does not support web fallback")
- else {
- val url = method.encodeToUri(
+ else
+ launchWebSDK(context, method, appId, requestId)
+ }
+ }
+
+ private fun launchWebSDK(
+ context: Context,
+ method: Method<*>,
+ appId: String,
+ requestId: String
+ ) {
+ val exceptionHandler = CoroutineExceptionHandler { _, error ->
+ val err = BloctoSDKError.values().find { it.message == error.message }
+ ?: BloctoSDKError.UNEXPECTED_ERROR
+ method.onError(err)
+ }
+
+ MainScope().launch(exceptionHandler) {
+ val url = withContext(Dispatchers.IO) {
+ method.encodeToWebUri(
authority = Const.webSDKUrl(env),
appId = appId,
- requestId = requestId
+ requestId = requestId,
+ webSessionId = webSessionId
).build().toString()
- context.startActivity(WebSDKActivity.newIntent(context, requestId, url))
}
+ context.startActivity(WebSDKActivity.newIntent(context, requestId, url))
}
}
@@ -65,6 +91,9 @@ object BloctoSDK {
val method = requestMap[requestId] ?: return
requestMap.clear()
if (handleError(method, uri)) return
+ if (method is RequestAccountMethod) {
+ webSessionId = uri.getQueryParameter(Const.KEY_SESSION_ID)
+ }
method.handleCallback(uri)
}
@@ -79,6 +108,7 @@ object BloctoSDK {
@VisibleForTesting
fun resetForTesting() {
appId = null
+ webSessionId = null
env = BloctoEnv.PROD
}
}
diff --git a/core/src/main/java/com/portto/sdk/core/method/Method.kt b/core/src/main/java/com/portto/sdk/core/method/Method.kt
index bcfa410..70582e4 100644
--- a/core/src/main/java/com/portto/sdk/core/method/Method.kt
+++ b/core/src/main/java/com/portto/sdk/core/method/Method.kt
@@ -1,6 +1,7 @@
package com.portto.sdk.core.method
import android.net.Uri
+import androidx.annotation.WorkerThread
import com.portto.sdk.core.Blockchain
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.Const
@@ -24,5 +25,20 @@ abstract class Method(
.appendQueryParameter(Const.KEY_REQUEST_ID, requestId)
.appendQueryParameter(Const.KEY_METHOD, name)
.appendQueryParameter(Const.KEY_BLOCKCHAIN, blockchain.value)
+ .appendQueryParameter(Const.KEY_PLATFORM, Const.SDK_SOURCE)
+ }
+
+ @WorkerThread
+ open fun encodeToWebUri(
+ authority: String,
+ appId: String,
+ requestId: String,
+ webSessionId: String? = null
+ ): Uri.Builder {
+ return Uri.Builder()
+ .scheme(Const.HTTPS_SCHEME)
+ .authority(authority)
+ .appendPath(appId)
+ .appendPath(blockchain.value)
}
}
diff --git a/core/src/main/java/com/portto/sdk/core/method/RequestAccountMethod.kt b/core/src/main/java/com/portto/sdk/core/method/RequestAccountMethod.kt
index b5c796b..25fc4b5 100644
--- a/core/src/main/java/com/portto/sdk/core/method/RequestAccountMethod.kt
+++ b/core/src/main/java/com/portto/sdk/core/method/RequestAccountMethod.kt
@@ -22,4 +22,16 @@ class RequestAccountMethod(
}
onSuccess(address)
}
+
+ override fun encodeToWebUri(
+ authority: String,
+ appId: String,
+ requestId: String,
+ webSessionId: String?
+ ): Uri.Builder {
+ return super.encodeToWebUri(authority, appId, requestId, webSessionId)
+ .appendPath(Const.PATH_AUTHN)
+ .appendQueryParameter(Const.KEY_REQUEST_ID, requestId)
+ .appendQueryParameter(Const.KEY_REQUEST_SOURCE, Const.SDK_SOURCE)
+ }
}
diff --git a/core/src/main/java/com/portto/sdk/core/model/SendTransactionResponse.kt b/core/src/main/java/com/portto/sdk/core/model/SendTransactionResponse.kt
new file mode 100644
index 0000000..f239952
--- /dev/null
+++ b/core/src/main/java/com/portto/sdk/core/model/SendTransactionResponse.kt
@@ -0,0 +1,10 @@
+package com.portto.sdk.core.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SendTransactionResponse(
+ @SerialName("authorizationId")
+ val authorizationId: String
+)
diff --git a/core/src/main/java/com/portto/sdk/core/model/SignMessageResponse.kt b/core/src/main/java/com/portto/sdk/core/model/SignMessageResponse.kt
new file mode 100644
index 0000000..309fe15
--- /dev/null
+++ b/core/src/main/java/com/portto/sdk/core/model/SignMessageResponse.kt
@@ -0,0 +1,10 @@
+package com.portto.sdk.core.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SignMessageResponse(
+ @SerialName("signatureId")
+ val signatureId: String
+)
diff --git a/dependencies.gradle b/dependencies.gradle
index ee682c8..95e90d4 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -1,20 +1,20 @@
ext.versions = [
minSdk : 21,
compileSdk : 33,
- versionCode : 5,
- versionName : '0.4.0',
+ versionCode : 6,
+ versionName : '0.5.0',
- androidx_activity : '1.6.0',
- androidx_appcompat : '1.5.1',
- androidx_browser : '1.4.0',
- androidx_core : '1.9.0',
- androidx_fragment : '1.5.3',
- androidx_lifecycle : '2.5.1',
+ androidx_activity : '1.7.1',
+ androidx_appcompat : '1.6.1',
+ androidx_browser : '1.5.0',
+ androidx_core : '1.10.1',
+ androidx_fragment : '1.5.7',
+ androidx_lifecycle : '2.6.1',
androidx_viewmodel : '2.4.1',
androidx_constraint : '2.1.4',
coroutine : '1.6.4',
flow_sdk : '0.7.1',
- material : '1.6.1',
+ material : '1.9.0',
okhttp : '4.10.0',
serialization : '1.3.2',
timber : '5.0.1',
diff --git a/evm/build.gradle b/evm/build.gradle
index d8fadda..9644235 100644
--- a/evm/build.gradle
+++ b/evm/build.gradle
@@ -1,6 +1,7 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
+ id 'kotlinx-serialization'
}
apply from: "$rootDir/dependencies.gradle"
diff --git a/evm/src/main/java/com/portto/sdk/evm/Avalanche.kt b/evm/src/main/java/com/portto/sdk/evm/Avalanche.kt
deleted file mode 100644
index 08ee5ba..0000000
--- a/evm/src/main/java/com/portto/sdk/evm/Avalanche.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.portto.sdk.evm
-
-import com.portto.sdk.core.Blockchain
-import com.portto.sdk.core.BloctoSDK
-
-val BloctoSDK.avalanche by lazy { Avalanche() }
-
-class Avalanche : Evm() {
-
- override val blockchain: Blockchain
- get() = Blockchain.AVALANCHE
-}
diff --git a/evm/src/main/java/com/portto/sdk/evm/BNBChain.kt b/evm/src/main/java/com/portto/sdk/evm/BNBChain.kt
deleted file mode 100644
index 7b43d46..0000000
--- a/evm/src/main/java/com/portto/sdk/evm/BNBChain.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.portto.sdk.evm
-
-import com.portto.sdk.core.Blockchain
-import com.portto.sdk.core.BloctoSDK
-
-val BloctoSDK.bnb by lazy { BNBChain() }
-
-class BNBChain : Evm() {
-
- override val blockchain: Blockchain
- get() = Blockchain.BNB_CHAIN
-}
diff --git a/evm/src/main/java/com/portto/sdk/evm/Ethereum.kt b/evm/src/main/java/com/portto/sdk/evm/Ethereum.kt
deleted file mode 100644
index be75287..0000000
--- a/evm/src/main/java/com/portto/sdk/evm/Ethereum.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.portto.sdk.evm
-
-import com.portto.sdk.core.Blockchain
-import com.portto.sdk.core.BloctoSDK
-
-val BloctoSDK.ethereum by lazy { Ethereum() }
-
-class Ethereum : Evm() {
-
- override val blockchain: Blockchain
- get() = Blockchain.ETHEREUM
-}
diff --git a/evm/src/main/java/com/portto/sdk/evm/Evm.kt b/evm/src/main/java/com/portto/sdk/evm/Evm.kt
index 5af38e0..ec5e817 100644
--- a/evm/src/main/java/com/portto/sdk/evm/Evm.kt
+++ b/evm/src/main/java/com/portto/sdk/evm/Evm.kt
@@ -1,9 +1,8 @@
package com.portto.sdk.evm
import android.content.Context
-import com.portto.sdk.core.Account
+import com.portto.sdk.core.Blockchain
import com.portto.sdk.core.BloctoSDK
-import com.portto.sdk.core.Chain
import com.portto.sdk.core.isValidHex
import com.portto.sdk.core.method.RequestAccountMethod
import com.portto.sdk.evm.method.SendTransactionMethod
@@ -12,10 +11,13 @@ import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.evm.EvmSignType
import java.math.BigInteger
-abstract class Evm : Chain, Account {
+val BloctoSDK.evm by lazy { Evm() }
- override fun requestAccount(
+class Evm {
+
+ fun requestAccount(
context: Context,
+ blockchain: Blockchain,
onSuccess: (String) -> Unit,
onError: (BloctoSDKError) -> Unit
) {
@@ -29,6 +31,7 @@ abstract class Evm : Chain, Account {
fun signMessage(
context: Context,
+ blockchain: Blockchain,
fromAddress: String,
signType: EvmSignType,
message: String,
@@ -52,6 +55,7 @@ abstract class Evm : Chain, Account {
fun sendTransaction(
context: Context,
+ blockchain: Blockchain,
fromAddress: String,
toAddress: String,
data: String,
diff --git a/evm/src/main/java/com/portto/sdk/evm/Polygon.kt b/evm/src/main/java/com/portto/sdk/evm/Polygon.kt
deleted file mode 100644
index 67c8175..0000000
--- a/evm/src/main/java/com/portto/sdk/evm/Polygon.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.portto.sdk.evm
-
-import com.portto.sdk.core.Blockchain
-import com.portto.sdk.core.BloctoSDK
-
-val BloctoSDK.polygon by lazy { Polygon() }
-
-class Polygon : Evm() {
-
- override val blockchain: Blockchain
- get() = Blockchain.POLYGON
-}
diff --git a/evm/src/main/java/com/portto/sdk/evm/method/SendTransactionMethod.kt b/evm/src/main/java/com/portto/sdk/evm/method/SendTransactionMethod.kt
index 03c74aa..b37eee7 100644
--- a/evm/src/main/java/com/portto/sdk/evm/method/SendTransactionMethod.kt
+++ b/evm/src/main/java/com/portto/sdk/evm/method/SendTransactionMethod.kt
@@ -1,8 +1,13 @@
package com.portto.sdk.evm.method
import android.net.Uri
+import androidx.annotation.WorkerThread
import com.portto.sdk.core.Blockchain
+import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.method.Method
+import com.portto.sdk.core.model.SendTransactionResponse
+import com.portto.sdk.core.post
+import com.portto.sdk.evm.model.SendTransactionRequest
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.Const
import java.math.BigInteger
@@ -12,7 +17,7 @@ class SendTransactionMethod(
private val toAddress: String,
private val data: String,
private val value: BigInteger = BigInteger.ZERO,
- blockchain: Blockchain,
+ private val blockchain: Blockchain,
onSuccess: (String) -> Unit,
onError: (BloctoSDKError) -> Unit
) : Method(blockchain, onSuccess, onError) {
@@ -36,4 +41,35 @@ class SendTransactionMethod(
.appendQueryParameter(Const.KEY_DATA, data)
.appendQueryParameter(Const.KEY_VALUE, String.format("%#x", value))
}
+
+ @WorkerThread
+ override fun encodeToWebUri(
+ authority: String,
+ appId: String,
+ requestId: String,
+ webSessionId: String?
+ ): Uri.Builder {
+ val sessionId = webSessionId ?: kotlin.run {
+ throw Throwable(BloctoSDKError.SESSION_ID_REQUIRED.message)
+ }
+
+ val requestBody = SendTransactionRequest(
+ from = fromAddress,
+ to = toAddress,
+ data = data,
+ value = String.format("%#x", value)
+ )
+
+ val headers = mapOf(
+ Const.HEADER_SESSION_ID to sessionId,
+ Const.HEADER_REQUEST_ID to requestId,
+ Const.HEADER_REQUEST_SOURCE to Const.SDK_SOURCE
+ )
+
+ val url = "${Const.webApiUrl(BloctoSDK.env)}/${blockchain.value}/${Const.PATH_DAPP}/${Const.PATH_AUTHZ}"
+ val response: SendTransactionResponse = post(url, listOf(requestBody), headers)
+ return super.encodeToWebUri(authority, appId, requestId, webSessionId)
+ .appendPath(Const.PATH_AUTHZ)
+ .appendPath(response.authorizationId)
+ }
}
diff --git a/evm/src/main/java/com/portto/sdk/evm/method/SignMessageMethod.kt b/evm/src/main/java/com/portto/sdk/evm/method/SignMessageMethod.kt
index c22b266..7b4ce67 100644
--- a/evm/src/main/java/com/portto/sdk/evm/method/SignMessageMethod.kt
+++ b/evm/src/main/java/com/portto/sdk/evm/method/SignMessageMethod.kt
@@ -1,8 +1,13 @@
package com.portto.sdk.evm.method
import android.net.Uri
+import androidx.annotation.WorkerThread
import com.portto.sdk.core.Blockchain
+import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.method.Method
+import com.portto.sdk.core.model.SignMessageResponse
+import com.portto.sdk.core.post
+import com.portto.sdk.evm.model.SignMessageRequest
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.Const
import com.portto.sdk.wallet.evm.EvmSignType
@@ -11,7 +16,7 @@ class SignMessageMethod(
private val fromAddress: String,
private val signType: EvmSignType,
private val message: String,
- blockchain: Blockchain,
+ private val blockchain: Blockchain,
onSuccess: (String) -> Unit,
onError: (BloctoSDKError) -> Unit
) : Method(blockchain, onSuccess, onError) {
@@ -34,4 +39,34 @@ class SignMessageMethod(
.appendQueryParameter(Const.KEY_TYPE, signType.type)
.appendQueryParameter(Const.KEY_MESSAGE, message)
}
+
+ @WorkerThread
+ override fun encodeToWebUri(
+ authority: String,
+ appId: String,
+ requestId: String,
+ webSessionId: String?
+ ): Uri.Builder {
+ val sessionId = webSessionId ?: kotlin.run {
+ throw Throwable(BloctoSDKError.SESSION_ID_REQUIRED.message)
+ }
+
+ val requestBody = SignMessageRequest(
+ from = fromAddress,
+ message = message,
+ method = signType.type
+ )
+
+ val headers = mapOf(
+ Const.HEADER_SESSION_ID to sessionId,
+ Const.HEADER_REQUEST_ID to requestId,
+ Const.HEADER_REQUEST_SOURCE to Const.SDK_SOURCE
+ )
+
+ val url = "${Const.webApiUrl(BloctoSDK.env)}/${blockchain.value}/${Const.PATH_DAPP}/${Const.PATH_USER_SIGNATURE}"
+ val response: SignMessageResponse = post(url, requestBody, headers)
+ return super.encodeToWebUri(authority, appId, requestId, webSessionId)
+ .appendPath(Const.PATH_USER_SIGNATURE)
+ .appendPath(response.signatureId)
+ }
}
diff --git a/evm/src/main/java/com/portto/sdk/evm/model/SendTransactionRequest.kt b/evm/src/main/java/com/portto/sdk/evm/model/SendTransactionRequest.kt
new file mode 100644
index 0000000..0448132
--- /dev/null
+++ b/evm/src/main/java/com/portto/sdk/evm/model/SendTransactionRequest.kt
@@ -0,0 +1,16 @@
+package com.portto.sdk.evm.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+class SendTransactionRequest(
+ @SerialName("from")
+ val from: String,
+ @SerialName("to")
+ val to: String,
+ @SerialName("data")
+ val data: String,
+ @SerialName("value")
+ val value: String
+)
diff --git a/evm/src/main/java/com/portto/sdk/evm/model/SignMessageRequest.kt b/evm/src/main/java/com/portto/sdk/evm/model/SignMessageRequest.kt
new file mode 100644
index 0000000..f5b23bb
--- /dev/null
+++ b/evm/src/main/java/com/portto/sdk/evm/model/SignMessageRequest.kt
@@ -0,0 +1,14 @@
+package com.portto.sdk.evm.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SignMessageRequest(
+ @SerialName("from")
+ val from: String,
+ @SerialName("message")
+ val message: String,
+ @SerialName("method")
+ val method: String
+)
diff --git a/flow/src/main/java/com/portto/sdk/flow/FlowService.kt b/flow/src/main/java/com/portto/sdk/flow/FlowService.kt
index bc7661a..339f333 100644
--- a/flow/src/main/java/com/portto/sdk/flow/FlowService.kt
+++ b/flow/src/main/java/com/portto/sdk/flow/FlowService.kt
@@ -1,11 +1,14 @@
package com.portto.sdk.flow
import androidx.annotation.WorkerThread
+import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.get
import com.portto.sdk.flow.model.FeePayerResponse
-
+import com.portto.sdk.wallet.Const
object FlowService {
@WorkerThread
- fun getFeePayer(): FeePayerResponse = get("flow/feePayer")
-}
\ No newline at end of file
+ fun getFeePayer(): FeePayerResponse = get(
+ url = "${Const.bloctoApiUrl(BloctoSDK.env)}/flow/feePayer"
+ )
+}
diff --git a/gradle.properties b/gradle.properties
index 773b72f..4986f87 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,7 +21,8 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
-android.disableAutomaticComponentCreation=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonFinalResIds=false
SONATYPE_HOST=S01
RELEASE_SIGNING_ENABLED=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 47ccf25..83a8ada 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Mar 09 10:37:29 CST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/scripts/distribute.sh b/scripts/distribute.sh
new file mode 100644
index 0000000..c67c112
--- /dev/null
+++ b/scripts/distribute.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+./gradlew clean app:assembleRelease app:appDistributionUploadRelease
diff --git a/scripts/publish.sh b/scripts/publish.sh
new file mode 100644
index 0000000..6538c12
--- /dev/null
+++ b/scripts/publish.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+# Build and upload the artifacts to 'mavenCentral'.
+./gradlew clean publish --no-daemon --no-parallel
diff --git a/solana/src/main/java/com/portto/sdk/solana/SolanaService.kt b/solana/src/main/java/com/portto/sdk/solana/SolanaService.kt
index aba8fde..26b89de 100644
--- a/solana/src/main/java/com/portto/sdk/solana/SolanaService.kt
+++ b/solana/src/main/java/com/portto/sdk/solana/SolanaService.kt
@@ -1,14 +1,17 @@
package com.portto.sdk.solana
import androidx.annotation.WorkerThread
+import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.post
import com.portto.sdk.solana.model.SolanaRawTxRequest
import com.portto.sdk.solana.model.SolanaRawTxResponse
+import com.portto.sdk.wallet.Const
object SolanaService {
@WorkerThread
fun createRawTransaction(requestBody: SolanaRawTxRequest): SolanaRawTxResponse {
- return post("/solana/createRawTransaction", requestBody)
+ val url = "${Const.bloctoApiUrl(BloctoSDK.env)}/solana/createRawTransaction"
+ return post(url, requestBody)
}
-}
\ No newline at end of file
+}
diff --git a/solana/src/main/java/com/portto/sdk/solana/method/SignAndSendTransactionMethod.kt b/solana/src/main/java/com/portto/sdk/solana/method/SignAndSendTransactionMethod.kt
index cc68615..2d66b4f 100644
--- a/solana/src/main/java/com/portto/sdk/solana/method/SignAndSendTransactionMethod.kt
+++ b/solana/src/main/java/com/portto/sdk/solana/method/SignAndSendTransactionMethod.kt
@@ -1,8 +1,13 @@
package com.portto.sdk.solana.method
import android.net.Uri
+import androidx.annotation.WorkerThread
import com.portto.sdk.core.Blockchain
+import com.portto.sdk.core.BloctoSDK
import com.portto.sdk.core.method.Method
+import com.portto.sdk.core.model.SendTransactionResponse
+import com.portto.sdk.core.post
+import com.portto.sdk.solana.model.SendTransactionRequest
import com.portto.sdk.wallet.BloctoSDKError
import com.portto.sdk.wallet.Const
@@ -12,7 +17,7 @@ class SignAndSendTransactionMethod(
val isInvokeWrapped: Boolean,
val publicKeySignaturePairs: Map? = null,
val appendTx: Map? = null,
- blockchain: Blockchain,
+ private val blockchain: Blockchain,
onSuccess: (String) -> Unit,
onError: (BloctoSDKError) -> Unit
) : Method(blockchain, onSuccess, onError) {
@@ -49,4 +54,36 @@ class SignAndSendTransactionMethod(
}
}
}
+
+ @WorkerThread
+ override fun encodeToWebUri(
+ authority: String,
+ appId: String,
+ requestId: String,
+ webSessionId: String?
+ ): Uri.Builder {
+ val sessionId = webSessionId ?: kotlin.run {
+ throw Throwable(BloctoSDKError.SESSION_ID_REQUIRED.message)
+ }
+
+ val requestBody = SendTransactionRequest(
+ from = fromAddress,
+ message = message,
+ isInvokeWrapped = isInvokeWrapped,
+ publicKeySignaturePairs = publicKeySignaturePairs,
+ appendTx = appendTx
+ )
+
+ val headers = mapOf(
+ Const.HEADER_SESSION_ID to sessionId,
+ Const.HEADER_REQUEST_ID to requestId,
+ Const.HEADER_REQUEST_SOURCE to Const.SDK_SOURCE
+ )
+
+ val url = "${Const.webApiUrl(BloctoSDK.env)}/${blockchain.value}/${Const.PATH_DAPP}/${Const.PATH_AUTHZ}"
+ val response: SendTransactionResponse = post(url, requestBody, headers)
+ return super.encodeToWebUri(authority, appId, requestId, webSessionId)
+ .appendPath(Const.PATH_AUTHZ)
+ .appendPath(response.authorizationId)
+ }
}
diff --git a/solana/src/main/java/com/portto/sdk/solana/model/SendTransactionRequest.kt b/solana/src/main/java/com/portto/sdk/solana/model/SendTransactionRequest.kt
new file mode 100644
index 0000000..46c0c41
--- /dev/null
+++ b/solana/src/main/java/com/portto/sdk/solana/model/SendTransactionRequest.kt
@@ -0,0 +1,18 @@
+package com.portto.sdk.solana.model
+
+import kotlinx.serialization.SerialName
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class SendTransactionRequest(
+ @SerialName("from")
+ val from: String,
+ @SerialName("message")
+ val message: String,
+ @SerialName("isInvokeWrapped")
+ val isInvokeWrapped: Boolean,
+ @SerialName("publicKeySignaturePairs")
+ val publicKeySignaturePairs: Map?,
+ @SerialName("appendTx")
+ val appendTx: Map?
+)
diff --git a/wallet/src/main/java/com/portto/sdk/wallet/BloctoSDKError.kt b/wallet/src/main/java/com/portto/sdk/wallet/BloctoSDKError.kt
index d1bd1fe..6cf916d 100644
--- a/wallet/src/main/java/com/portto/sdk/wallet/BloctoSDKError.kt
+++ b/wallet/src/main/java/com/portto/sdk/wallet/BloctoSDKError.kt
@@ -8,4 +8,8 @@ enum class BloctoSDKError(val message: String) {
UNEXPECTED_ERROR("unexpected_error"),
ETH_SIGN_INVALID_HEX_STRING("eth_sign_invalid_hex_string"),
FLOW_MISSING_ARG("flow_app_id_and_nonce_are_required_by_authn"),
+ SESSION_ID_REQUIRED("session_id_required"),
+ INVALID_SESSION_ID("invalid_session_id"),
+ MESSAGE_REQUIRED("message_required"),
+ METHOD_NOT_SUPPORTED("method_not_supported"),
}
diff --git a/wallet/src/main/java/com/portto/sdk/wallet/Const.kt b/wallet/src/main/java/com/portto/sdk/wallet/Const.kt
index 887b857..8b63c72 100644
--- a/wallet/src/main/java/com/portto/sdk/wallet/Const.kt
+++ b/wallet/src/main/java/com/portto/sdk/wallet/Const.kt
@@ -17,12 +17,19 @@ object Const {
private const val BLOCTO_URI_AUTHORITY_PROD = "blocto.app"
private const val BLOCTO_URI_AUTHORITY_DEV = "dev.blocto.app"
- private const val WEB_SDK_URL_PROD = "wallet.blocto.app"
- private const val WEB_SDK_URL_DEV = "wallet-testnet.blocto.app"
+ private const val BLOCTO_API_URL_PROD = "https://api.blocto.app"
+ private const val BLOCTO_API_URL_DEV = "https://api-dev.blocto.app"
+
+ private const val WEB_SDK_URL_PROD = "wallet-v2.blocto.app"
+ private const val WEB_SDK_URL_DEV = "wallet-v2-dev.blocto.app"
+
+ private const val WEB_API_URL_PROD = "https://wallet-v2.blocto.app/api"
+ private const val WEB_API_URL_DEV = "https://wallet-v2-dev.blocto.app/api"
const val HTTPS_SCHEME = "https"
const val BLOCTO_SCHEME = "blocto"
const val BLOCTO_URI_PATH = "sdk"
+ const val SDK_SOURCE = "sdk_android"
const val KEY_APP_ID = "app_id"
const val KEY_REQUEST_ID = "request_id"
@@ -47,7 +54,18 @@ object Const {
const val KEY_FLOW_APP_ID = "flow_app_id" // Since 0.3.0 (Flow)
const val KEY_FLOW_NONCE = "flow_nonce" // Since 0.3.0 (Flow)
const val KEY_FLOW_TX = "flow_transaction" // Since 0.3.0 (Flow)
+ const val KEY_SESSION_ID = "session_id"
+ const val KEY_REQUEST_SOURCE = "request_source"
+ const val KEY_PLATFORM = "platform"
+ const val PATH_DAPP = "dapp"
+ const val PATH_AUTHN = "authn"
+ const val PATH_AUTHZ = "authz"
+ const val PATH_USER_SIGNATURE = "user-signature"
+
+ const val HEADER_SESSION_ID = "Blocto-Session-Identifier"
+ const val HEADER_REQUEST_ID = "Blocto-Request-Identifier"
+ const val HEADER_REQUEST_SOURCE = "Blocto-Request-Source"
fun bloctoAuthority(env: BloctoEnv): String = when (env) {
BloctoEnv.PROD -> BLOCTO_URI_AUTHORITY_PROD
@@ -59,8 +77,18 @@ object Const {
BloctoEnv.DEV -> BLOCTO_PACKAGE_DEV
}
+ fun bloctoApiUrl(env: BloctoEnv): String = when (env) {
+ BloctoEnv.PROD -> BLOCTO_API_URL_PROD
+ BloctoEnv.DEV -> BLOCTO_API_URL_DEV
+ }
+
fun webSDKUrl(env: BloctoEnv): String = when (env) {
BloctoEnv.PROD -> WEB_SDK_URL_PROD
BloctoEnv.DEV -> WEB_SDK_URL_DEV
}
+
+ fun webApiUrl(env: BloctoEnv): String = when (env) {
+ BloctoEnv.PROD -> WEB_API_URL_PROD
+ BloctoEnv.DEV -> WEB_API_URL_DEV
+ }
}