├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .prettierrc
├── .releaserc.json
├── .vscode
└── settings.json
├── README.md
├── android
├── app
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ └── com
│ │ │ └── trust
│ │ │ └── web3
│ │ │ └── demo
│ │ │ ├── App.kt
│ │ │ ├── DAppMethod.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── Numeric.kt
│ │ │ ├── WebAppInterface.kt
│ │ │ └── WebViewExtension.kt
│ │ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── lib
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── res
│ │ └── raw
│ │ └── trust_min.js
└── settings.gradle
├── bun.lockb
├── bunfig.toml
├── docs
├── BUILD.md
├── NEW.md
└── USAGE.md
├── eslint.config.js
├── jitpack.yml
├── package.json
├── packages
├── android-web3-provider
│ ├── package.json
│ ├── rollup.config.js
│ ├── rollup.config2.js
│ ├── settings.gradle
│ ├── src
│ │ └── index.ts
│ └── tsconfig.json
├── aptos
│ ├── AptosProvider.ts
│ ├── README.md
│ ├── exceptions
│ │ └── RPCError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ │ └── AptosProvider.spec.ts
│ ├── tsconfig.json
│ └── types
│ │ └── AptosProvider.ts
├── core
│ ├── Provider.ts
│ ├── README.md
│ ├── adapter
│ │ ├── Adapter.ts
│ │ ├── CallbackAdapter.ts
│ │ ├── PromiseAdapter.ts
│ │ └── tests
│ │ │ ├── CallbackBridge.spec.ts
│ │ │ └── PromiseBridge.spec.ts
│ ├── exceptions
│ │ └── RPCError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ └── tsconfig.json
├── cosmos
│ ├── CosmosProvider.ts
│ ├── MobileAdapter.ts
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ │ └── CosmosProvider.spec.ts
│ ├── tsconfig.json
│ └── types
│ │ └── CosmosProvider.ts
├── ethereum
│ ├── EthereumProvider.ts
│ ├── MobileAdapter.ts
│ ├── README.md
│ ├── RPCServer.ts
│ ├── exceptions
│ │ └── RPCError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ │ ├── EthereumProvider.spec.ts
│ │ └── mocks
│ │ │ └── Wallet.ts
│ ├── tsconfig.json
│ ├── types.ts
│ └── types
│ │ ├── EthereumProvider.ts
│ │ └── RPC.ts
├── ios-web3-provider
│ ├── .swiftpm
│ │ └── xcode
│ │ │ └── xcuserdata
│ │ │ └── fivezone.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── Package.swift
│ ├── README.md
│ ├── TrustWeb3Provider.podspec
│ ├── global.d.ts
│ ├── index.ts
│ ├── ios
│ │ ├── Packages
│ │ │ └── WalletCore
│ │ │ │ ├── .swiftpm
│ │ │ │ └── xcode
│ │ │ │ │ └── xcuserdata
│ │ │ │ │ └── fivezone.xcuserdatad
│ │ │ │ │ └── xcschemes
│ │ │ │ │ └── xcschememanagement.plist
│ │ │ │ └── Package.swift
│ │ ├── PodsTest
│ │ │ ├── Podfile
│ │ │ ├── Podfile.lock
│ │ │ ├── PodsTest.xcodeproj
│ │ │ │ ├── project.pbxproj
│ │ │ │ ├── project.xcworkspace
│ │ │ │ │ └── contents.xcworkspacedata
│ │ │ │ └── xcshareddata
│ │ │ │ │ └── xcschemes
│ │ │ │ │ └── PodsTest.xcscheme
│ │ │ ├── PodsTest.xcworkspace
│ │ │ │ └── contents.xcworkspacedata
│ │ │ ├── PodsTest
│ │ │ │ ├── Assets.xcassets
│ │ │ │ │ ├── AccentColor.colorset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ ├── AppIcon.appiconset
│ │ │ │ │ │ └── Contents.json
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── ContentView.swift
│ │ │ │ ├── PodsTestApp.swift
│ │ │ │ └── Preview Content
│ │ │ │ │ └── Preview Assets.xcassets
│ │ │ │ │ └── Contents.json
│ │ │ └── PodsTestTests
│ │ │ │ └── PodsTestTests.swift
│ │ ├── TrustWeb3Provider.xcodeproj
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ ├── xcshareddata
│ │ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ │ └── xcuserdata
│ │ │ │ │ └── fivezone.xcuserdatad
│ │ │ │ │ └── UserInterfaceState.xcuserstate
│ │ │ ├── xcshareddata
│ │ │ │ └── xcschemes
│ │ │ │ │ └── TrustWeb3Provider-Example.xcscheme
│ │ │ └── xcuserdata
│ │ │ │ └── fivezone.xcuserdatad
│ │ │ │ └── xcschemes
│ │ │ │ └── xcschememanagement.plist
│ │ └── TrustWeb3Provider
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.xib
│ │ │ └── Main.storyboard
│ │ │ ├── DAppMethod.swift
│ │ │ ├── DAppWebViewController.swift
│ │ │ ├── Images.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ ├── Info.plist
│ │ │ └── WKScriptMessage+JSON.swift
│ ├── package.json
│ ├── rollup.config.js
│ ├── swift
│ │ ├── ProviderNetwork.swift
│ │ ├── TrustWeb3Provider.swift
│ │ ├── WKWebView+Extension.swift
│ │ └── trust-min.js
│ └── tsconfig.json
├── solana
│ ├── MobileAdapter.ts
│ ├── README.md
│ ├── SolanaProvider.ts
│ ├── adapter
│ │ ├── account.ts
│ │ ├── icon.ts
│ │ ├── initialize.ts
│ │ ├── register.ts
│ │ ├── solana.ts
│ │ ├── wallet.ts
│ │ └── window.ts
│ ├── exceptions
│ │ └── RPCError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ │ ├── SolanaProvider.spec.ts
│ │ └── mocks
│ │ │ └── window.ts
│ ├── tsconfig.json
│ ├── types
│ │ └── SolanaProvider.ts
│ └── util.ts
├── ton
│ ├── MobileAdapter.ts
│ ├── README.md
│ ├── TonBridge.ts
│ ├── TonProvider.ts
│ ├── exceptions
│ │ ├── RPCError.ts
│ │ └── TonConnectError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ │ └── TonProvider.spec.ts
│ ├── tsconfig.json
│ └── types
│ │ ├── TonBridge.ts
│ │ └── TonProvider.ts
└── tron
│ ├── TronProvider.ts
│ ├── exceptions
│ └── RPCError.ts
│ ├── index.ts
│ ├── package.json
│ ├── rollup.config.js
│ ├── tests
│ └── TronProvider.spec.ts
│ ├── tsconfig.json
│ └── types
│ └── TronProvider.ts
├── rollup.config.js
├── scripts
├── build.ts
├── generate.ts
├── link.ts
├── packages.ts
├── publish.ts
└── rename.ts
├── templates
├── exceptions
│ └── RPCError.ts
├── index.ts
├── package.json
├── rollup.config.js
├── tests
│ └── {{ name }}Provider.spec.ts
├── tsconfig.json
├── types
│ └── {{ name}}Provider.ts
└── {{ name }}Provider.ts
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | trust-min.js filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build Packages
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | env:
7 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 |
9 | jobs:
10 | github-release:
11 | name: Github Release
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | ref: main
17 | - uses: oven-sh/setup-bun@v1
18 | - uses: actions/setup-node@v4
19 | with:
20 | node-version: '20.x'
21 | registry-url: 'https://npm.pkg.github.com'
22 | scope: '@trustwallet'
23 | - name: Generate version
24 | id: get-next-version
25 | run: |
26 | bun install --frozen-lockfile
27 | bun run build:version
28 | env:
29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30 | - name: Set output
31 | id: vars
32 | run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
33 | - name: build packages
34 | run: |
35 | bun install
36 | bun run build:packages
37 | bun run test
38 | bun run rename ${{ steps.get-next-version.outputs.new-release-version }}
39 | bun run publish
40 | env:
41 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
42 | - name: Create Release
43 | id: create_release
44 | uses: actions/create-release@latest
45 | env:
46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47 | with:
48 | tag_name: v${{ steps.get-next-version.outputs.new-release-version }}
49 | release_name:
50 | Release ${{ steps.get-next-version.outputs.new-release-version }}
51 | body: |
52 | New Release 🚀
53 | draft: false
54 | prerelease: false
55 | permissions:
56 | actions: write
57 | contents: write
58 | deployments: write
59 | packages: write
60 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
2 |
3 | # Logs
4 |
5 | logs
6 | _.log
7 | npm-debug.log_
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 | .pnpm-debug.log*
12 |
13 | # Caches
14 |
15 | .cache
16 |
17 | # Diagnostic reports (https://nodejs.org/api/report.html)
18 |
19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
20 |
21 | # Runtime data
22 |
23 | pids
24 | _.pid
25 | _.seed
26 | *.pid.lock
27 |
28 | # Directory for instrumented libs generated by jscoverage/JSCover
29 |
30 | lib-cov
31 |
32 | # Coverage directory used by tools like istanbul
33 |
34 | coverage
35 | *.lcov
36 |
37 | # nyc test coverage
38 |
39 | .nyc_output
40 |
41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
42 |
43 | .grunt
44 |
45 | # Bower dependency directory (https://bower.io/)
46 |
47 | bower_components
48 |
49 | # node-waf configuration
50 |
51 | .lock-wscript
52 |
53 | # Compiled binary addons (https://nodejs.org/api/addons.html)
54 |
55 | build/Release
56 |
57 | # Dependency directories
58 |
59 | node_modules/
60 | jspm_packages/
61 |
62 | # Snowpack dependency directory (https://snowpack.dev/)
63 |
64 | web_modules/
65 |
66 | # TypeScript cache
67 |
68 | *.tsbuildinfo
69 |
70 | # Optional npm cache directory
71 |
72 | .npm
73 |
74 | # Optional eslint cache
75 |
76 | .eslintcache
77 |
78 | # Optional stylelint cache
79 |
80 | .stylelintcache
81 |
82 | # Microbundle cache
83 |
84 | .rpt2_cache/
85 | .rts2_cache_cjs/
86 | .rts2_cache_es/
87 | .rts2_cache_umd/
88 |
89 | # Optional REPL history
90 |
91 | .node_repl_history
92 |
93 | # Output of 'npm pack'
94 |
95 | *.tgz
96 |
97 | # Yarn Integrity file
98 |
99 | .yarn-integrity
100 |
101 | # dotenv environment variable files
102 |
103 | .env
104 | .env.development.local
105 | .env.test.local
106 | .env.production.local
107 | .env.local
108 |
109 | # parcel-bundler cache (https://parceljs.org/)
110 |
111 | .parcel-cache
112 |
113 | # Next.js build output
114 |
115 | .next
116 | out
117 |
118 | # Nuxt.js build / generate output
119 |
120 | .nuxt
121 | dist
122 |
123 | # Gatsby files
124 |
125 | # Comment in the public line in if your project uses Gatsby and not Next.js
126 |
127 | # https://nextjs.org/blog/next-9-1#public-directory-support
128 |
129 | # public
130 |
131 | # vuepress build output
132 |
133 | .vuepress/dist
134 |
135 | # vuepress v2.x temp and cache directory
136 |
137 | .temp
138 |
139 | # Docusaurus cache and generated files
140 |
141 | .docusaurus
142 |
143 | # Serverless directories
144 |
145 | .serverless/
146 |
147 | # FuseBox cache
148 |
149 | .fusebox/
150 |
151 | # DynamoDB Local files
152 |
153 | .dynamodb/
154 |
155 | # TernJS port file
156 |
157 | .tern-port
158 |
159 | # Stores VSCode versions used for testing VSCode extensions
160 |
161 | .vscode-test
162 |
163 | # yarn v2
164 |
165 | .yarn/cache
166 | .yarn/unplugged
167 | .yarn/build-state.yml
168 | .yarn/install-state.gz
169 | .pnp.*
170 |
171 | # IntelliJ based IDEs
172 | .idea
173 |
174 | # Finder (MacOS) folder config
175 | .DS_Store
176 | android/build
177 | android/app/build/*
178 | android/lib/build/*
179 | android/.gradle
180 | local.properties
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "printWidth": 80,
4 | "proseWrap": "always",
5 | "semi": true,
6 | "singleQuote": true,
7 | "trailingComma": "all",
8 | "tabWidth": 2,
9 | "useTabs": false,
10 | "bracketSpacing": true
11 | }
12 |
--------------------------------------------------------------------------------
/.releaserc.json:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "main",
4 | "hotfix/*",
5 | { "name": "release-v\\d+\\.\\d+\\.\\d+", "prerelease": true }
6 | ],
7 | "plugins": ["semantic-release-export-data"]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "Bounceable",
4 | "outdir",
5 | "Parens",
6 | "pmqjw",
7 | "prelink",
8 | "subpackages",
9 | "uuidv"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 | ___ ___ ___
5 | ___ / /\ / /\ / /\ ___
6 | /__/\ / /::\ / /:/ / /::\ /__/\
7 | \ \:\ / /:/\:\ / /:/ /__/:/\:\ \ \:\
8 | \__\:\ / /::\ \:\ / /:/ _\_ \:\ \:\ \__\:\
9 | / /::\ /__/:/\:\_\:\ /__/:/ /\ /__/\ \:\ \:\ / /::\
10 | / /:/\:\ \__\/~|::\/:/ \ \:\ /:/ \ \:\ \:\_\/ / /:/\:\
11 | / /:/__\/ | |:|::/ \ \:\ /:/ \ \:\_\:\ / /:/__\/
12 | /__/:/ | |:|\/ \ \:\/:/ \ \:\/:/ /__/:/
13 | \__\/ |__|:|~ \ \::/ \ \::/ \__\/
14 | \__\| \__\/ \__\/
15 |
16 | ```
17 |
18 | A modular TypeScript library designed to offer Web3 interfaces, enabling your
19 | wallet to connect with decentralized applications.
20 |
21 | ```
22 | +----------------+ +------------------+ +---------------+
23 | | | | | | |
24 | | dApps | <-----> | web3 provider | <-----> | your wallet |
25 | | | | | | |
26 | +----------------+ +------------------+ +---------------+
27 |
28 | ```
29 |
30 | ## Supported chains
31 |
32 | - Cosmos [Docs](/packages/cosmos/README.md)
33 | - Solana - _Wallet Standard fully compatible_ [Docs](/packages/solana/README.md)
34 | - Ethereum _EIP-1193_ [Docs](/packages/ethereum/README.md)
35 |
36 | # Useful links
37 |
38 | [Using the library](/docs/USAGE.md)
39 |
40 | [Contributing](/docs/BUILD.md)
41 |
42 | [Adding a new chain](/docs/NEW.md)
43 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 30
8 | buildToolsVersion "30.0.3"
9 |
10 | defaultConfig {
11 | applicationId "com.trust.web3.demo"
12 | minSdkVersion 23
13 | targetSdkVersion 30
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
37 | implementation 'androidx.core:core-ktx:1.2.0'
38 | implementation 'androidx.appcompat:appcompat:1.2.0'
39 | implementation 'com.google.android.material:material:1.1.0'
40 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
41 |
42 | implementation "com.louiscad.splitties:splitties-alertdialog-material:3.0.0"
43 | implementation "com.trustwallet:wallet-core:2.9.7"
44 |
45 | implementation 'com.github.trustwallet:trust-web3-provider:2.0.0-alpha'
46 | testImplementation 'junit:junit:4.+'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
49 | }
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/App.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | import android.app.Application
4 |
5 | class App: Application() {
6 | init {
7 | System.loadLibrary("TrustWalletCore")
8 | }
9 | }
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/DAppMethod.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | enum class DAppMethod {
4 | SIGNTRANSACTION,
5 | SIGNPERSONALMESSAGE,
6 | SIGNMESSAGE,
7 | SIGNTYPEDMESSAGE,
8 | ECRECOVER,
9 | REQUESTACCOUNTS,
10 | WATCHASSET,
11 | ADDETHEREUMCHAIN,
12 | UNKNOWN;
13 |
14 | companion object {
15 | fun fromValue(value: String): DAppMethod {
16 | return when (value) {
17 | "signTransaction" -> SIGNTRANSACTION
18 | "signPersonalMessage" -> SIGNPERSONALMESSAGE
19 | "signMessage" -> SIGNMESSAGE
20 | "signTypedMessage" -> SIGNTYPEDMESSAGE
21 | "ecRecover" -> ECRECOVER
22 | "requestAccounts" -> REQUESTACCOUNTS
23 | "watchAsset" -> WATCHASSET
24 | "addEthereumChain" -> ADDETHEREUMCHAIN
25 | else -> UNKNOWN
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | import android.graphics.Bitmap
4 | import android.net.http.SslError
5 | import android.os.Bundle
6 | import android.webkit.SslErrorHandler
7 | import android.webkit.WebView
8 | import android.webkit.WebViewClient
9 | import androidx.appcompat.app.AppCompatActivity
10 |
11 | class MainActivity : AppCompatActivity() {
12 | companion object {
13 | private const val DAPP_URL = "https://www.magiceden.io/me"
14 | private const val CHAIN_ID = 56
15 | private const val RPC_URL = "https://bsc-dataseed2.binance.org"
16 | }
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 |
21 | setContentView(R.layout.activity_main)
22 |
23 | val provderJs = loadProviderJs()
24 | val initJs = loadInitJs(
25 | CHAIN_ID,
26 | RPC_URL
27 | )
28 | WebView.setWebContentsDebuggingEnabled(true)
29 | val webview: WebView = findViewById(R.id.webview)
30 | webview.settings.run {
31 | javaScriptEnabled = true
32 | domStorageEnabled = true
33 | }
34 | WebAppInterface(this, webview, DAPP_URL).run {
35 | webview.addJavascriptInterface(this, "_tw_")
36 |
37 | val webViewClient = object : WebViewClient() {
38 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
39 | super.onPageStarted(view, url, favicon)
40 | view?.evaluateJavascript(provderJs, null)
41 | view?.evaluateJavascript(initJs, null)
42 | }
43 |
44 | override fun onReceivedSslError(
45 | view: WebView?,
46 | handler: SslErrorHandler?,
47 | error: SslError?
48 | ) {
49 | // Ignore SSL certificate errors
50 | handler?.proceed()
51 | println(error.toString())
52 | }
53 | }
54 | webview.webViewClient = webViewClient
55 | webview.loadUrl(DAPP_URL)
56 | }
57 | }
58 |
59 | private fun loadProviderJs(): String {
60 | return resources.openRawResource(R.raw.trust_min).bufferedReader().use { it.readText() }
61 | }
62 |
63 | private fun loadInitJs(chainId: Int, rpcUrl: String): String {
64 | val source = """
65 | (function() {
66 | var config = {
67 | ethereum: {
68 | chainId: $chainId,
69 | rpcUrl: "$rpcUrl"
70 | },
71 | solana: {
72 | cluster: "mainnet-beta",
73 | },
74 | isDebug: true
75 | };
76 | trustwallet.ethereum = new trustwallet.Provider(config);
77 | trustwallet.solana = new trustwallet.SolanaProvider(config);
78 | trustwallet.postMessage = (json) => {
79 | window._tw_.postMessage(JSON.stringify(json));
80 | }
81 | window.ethereum = trustwallet.ethereum;
82 | })();
83 | """
84 | return source
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/Numeric.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | import kotlin.experimental.and
4 |
5 | object Numeric {
6 | fun containsHexPrefix(input: String): Boolean {
7 | return input.length > 1 && input[0] == '0' && input[1] == 'x'
8 | }
9 |
10 | fun cleanHexPrefix(input: String): String {
11 | return if (containsHexPrefix(input)) {
12 | input.substring(2)
13 | } else {
14 | input
15 | }
16 | }
17 |
18 | fun hexStringToByteArray(input: String): ByteArray {
19 | val cleanInput = cleanHexPrefix(input)
20 |
21 | val len = cleanInput.length
22 |
23 | if (len == 0) {
24 | return byteArrayOf()
25 | }
26 |
27 | val data: ByteArray
28 | val startIdx: Int
29 | if (len % 2 != 0) {
30 | data = ByteArray(len / 2 + 1)
31 | data[0] = Character.digit(cleanInput[0], 16).toByte()
32 | startIdx = 1
33 | } else {
34 | data = ByteArray(len / 2)
35 | startIdx = 0
36 | }
37 |
38 | var i = startIdx
39 | while (i < len) {
40 | data[(i + 1) / 2] =
41 | ((Character.digit(cleanInput[i], 16) shl 4) + Character.digit(
42 | cleanInput[i + 1],
43 | 16
44 | )).toByte()
45 | i += 2
46 | }
47 | return data
48 | }
49 |
50 | fun toHexString(input: ByteArray?, offset: Int, length: Int, withPrefix: Boolean): String {
51 | val stringBuilder = StringBuilder()
52 | if (withPrefix) {
53 | stringBuilder.append("0x")
54 | }
55 | for (i in offset until offset + length) {
56 | stringBuilder.append(String.format("%02x", input!![i] and 0xFF.toByte()))
57 | }
58 |
59 | return stringBuilder.toString()
60 | }
61 |
62 | fun toHexString(input: ByteArray?): String {
63 | return toHexString(input, 0, input!!.size, true)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/WebAppInterface.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | import android.content.Context
4 | import android.webkit.JavascriptInterface
5 | import android.webkit.WebView
6 | import org.json.JSONObject
7 | import splitties.alertdialog.appcompat.cancelButton
8 | import splitties.alertdialog.appcompat.message
9 | import splitties.alertdialog.appcompat.okButton
10 | import splitties.alertdialog.appcompat.title
11 | import splitties.alertdialog.material.materialAlertDialog
12 | import wallet.core.jni.CoinType
13 | import wallet.core.jni.Curve
14 | import wallet.core.jni.PrivateKey
15 |
16 | class WebAppInterface(
17 | private val context: Context,
18 | private val webView: WebView,
19 | private val dappUrl: String
20 | ) {
21 | private val privateKey =
22 | PrivateKey("0x4646464646464646464646464646464646464646464646464646464646464646".toHexByteArray())
23 | private val addr = CoinType.ETHEREUM.deriveAddress(privateKey).toLowerCase()
24 | private val pubKey = CoinType.SOLANA.deriveAddress(privateKey)
25 |
26 | @JavascriptInterface
27 | fun postMessage(json: String) {
28 | val obj = JSONObject(json)
29 | println(obj)
30 | val id = obj.getLong("id")
31 | val method = DAppMethod.fromValue(obj.getString("name"))
32 | val network = obj.getString("network")
33 | when (method) {
34 | DAppMethod.REQUESTACCOUNTS -> {
35 | context.materialAlertDialog {
36 | title = "Request Accounts"
37 | message = "${dappUrl} requests your address"
38 | okButton {
39 | val address = if (network == "solana") pubKey else addr
40 | val setAddress = "window.$network.setAddress(\"$address\");"
41 | val callback = "window.$network.sendResponse($id, [\"$address\"])"
42 | webView.post {
43 | webView.evaluateJavascript(setAddress) {
44 | // ignore
45 | }
46 | webView.evaluateJavascript(callback) { value ->
47 | println(value)
48 | }
49 | }
50 | }
51 | cancelButton()
52 | }.show()
53 | }
54 | DAppMethod.SIGNMESSAGE -> {
55 | val data = extractMessage(obj)
56 | if (network == "ethereum")
57 | handleSignMessage(id, data, addPrefix = false)
58 | else
59 | handleSignSolanaMessage(id, data)
60 | }
61 | DAppMethod.SIGNPERSONALMESSAGE -> {
62 | val data = extractMessage(obj)
63 | handleSignMessage(id, data, addPrefix = true)
64 | }
65 | DAppMethod.SIGNTYPEDMESSAGE -> {
66 | val data = extractMessage(obj)
67 | val raw = extractRaw(obj)
68 | handleSignTypedMessage(id, data, raw)
69 | }
70 | else -> {
71 | context.materialAlertDialog {
72 | title = "Error"
73 | message = "$method not implemented"
74 | okButton {
75 | }
76 | }.show()
77 | }
78 | }
79 | }
80 |
81 | private fun extractMessage(json: JSONObject): ByteArray {
82 | val param = json.getJSONObject("object")
83 | val data = param.getString("data")
84 | return Numeric.hexStringToByteArray(data)
85 | }
86 |
87 | private fun extractRaw(json: JSONObject): String {
88 | val param = json.getJSONObject("object")
89 | return param.getString("raw")
90 | }
91 |
92 | private fun handleSignMessage(id: Long, data: ByteArray, addPrefix: Boolean) {
93 | context.materialAlertDialog {
94 | title = "Sign Ethereum Message"
95 | message = if (addPrefix) String(data, Charsets.UTF_8) else Numeric.toHexString(data)
96 | cancelButton {
97 | webView.sendError("ethereum","Cancel", id)
98 | }
99 | okButton {
100 | webView.sendResult("ethereum", signEthereumMessage(data, addPrefix), id)
101 | }
102 | }.show()
103 | }
104 |
105 | private fun handleSignSolanaMessage(id: Long, data: ByteArray) {
106 | context.materialAlertDialog {
107 | title = "Sign Solana Message"
108 | message = String(data, Charsets.UTF_8) ?: Numeric.toHexString(data)
109 | cancelButton {
110 | webView.sendError("solana", "Cancel", id)
111 | }
112 | okButton {
113 | webView.sendResult("solana", signSolanaMessage(data), id)
114 | }
115 | }.show()
116 | }
117 |
118 | private fun handleSignTypedMessage(id: Long, data: ByteArray, raw: String) {
119 | context.materialAlertDialog {
120 | title = "Sign Typed Message"
121 | message = raw
122 | cancelButton {
123 | webView.sendError("ethereum","Cancel", id)
124 | }
125 | okButton {
126 | webView.sendResult("ethereum", signEthereumMessage(data, false), id)
127 | }
128 | }.show()
129 | }
130 |
131 | private fun signEthereumMessage(message: ByteArray, addPrefix: Boolean): String {
132 | var data = message
133 | if (addPrefix) {
134 | val messagePrefix = "\u0019Ethereum Signed Message:\n"
135 | val prefix = (messagePrefix + message.size).toByteArray()
136 | val result = ByteArray(prefix.size + message.size)
137 | System.arraycopy(prefix, 0, result, 0, prefix.size)
138 | System.arraycopy(message, 0, result, prefix.size, message.size)
139 | data = wallet.core.jni.Hash.keccak256(result)
140 | }
141 |
142 | val signatureData = privateKey.sign(data, Curve.SECP256K1)
143 | .apply {
144 | (this[this.size - 1]) = (this[this.size - 1] + 27).toByte()
145 | }
146 | return Numeric.toHexString(signatureData)
147 | }
148 |
149 | private fun signSolanaMessage(message: ByteArray): String {
150 | val signature = privateKey.sign(message, Curve.ED25519)
151 | return Numeric.toHexString(signature)
152 |
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/trust/web3/demo/WebViewExtension.kt:
--------------------------------------------------------------------------------
1 | package com.trust.web3.demo
2 |
3 | import android.webkit.WebView
4 |
5 | fun WebView.sendError(network: String, message: String, methodId: Long) {
6 | val script = "window.$network.sendError($methodId, \"$message\")"
7 | this.post {
8 | this.evaluateJavascript(script) {}
9 | }
10 | }
11 |
12 | fun WebView.sendResult(network: String, message: String, methodId: Long) {
13 | val script = "window.$network.sendResponse($methodId, \"$message\")"
14 | this.post {
15 | this.evaluateJavascript(script) {}
16 | }
17 | }
18 |
19 | fun WebView.sendResults(network: String, messages: List, methodId: Long) {
20 | val message = messages.joinToString(separator = ",")
21 | val script = "window.$network.sendResponse($methodId, \"$message\")"
22 | this.post {
23 | this.evaluateJavascript(script) {}
24 | }
25 | }
26 |
27 | fun String.toHexByteArray(): ByteArray {
28 | return Numeric.hexStringToByteArray(this)
29 | }
30 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Web3 Provider Demo
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = "1.4.31"
4 | repositories {
5 | google()
6 | mavenCentral()
7 |
8 | maven { url 'https://jitpack.io' }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.2.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 |
22 | maven { url 'https://jitpack.io' }
23 |
24 | maven {
25 | url = uri("https://maven.pkg.github.com/trustwallet/wallet-core")
26 | Properties properties = new Properties()
27 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
28 | credentials {
29 | username = properties.getProperty("gpr.user")
30 | password = properties.getProperty("gpr.key")
31 | }
32 | }
33 | }
34 | }
35 |
36 | task clean(type: Delete) {
37 | delete rootProject.buildDir
38 | }
39 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 | # Disable generation of the BuildConfig class
23 | android.defaults.buildfeatures.buildconfig=false
24 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trustwallet/trust-web3-provider/e94d1c57bed40b90deedf04d3e56c8c817be9886/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Mar 17 17:43:08 JST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/android/lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | apply plugin: 'maven-publish'
4 |
5 | group='com.trustwallet'
6 |
7 | def getArtificatId = { ->
8 | return "web3-provider"
9 | }
10 | def getVersionName = { ->
11 | return System.getenv('TAG_NAME') ?: "1.0.4"
12 | }
13 |
14 | android {
15 | compileSdkVersion 28
16 |
17 | defaultConfig {
18 | minSdkVersion 23
19 | targetSdkVersion 28
20 | versionCode 1
21 | project.archivesBaseName = getArtificatId()
22 | versionName getVersionName()
23 | }
24 |
25 | buildTypes {
26 | release {
27 | minifyEnabled false
28 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
29 | }
30 | }
31 |
32 | }
33 |
34 | dependencies {}
35 |
36 | publishing {
37 | publications {
38 | web3provider(MavenPublication) {
39 | groupId group
40 | artifactId getArtificatId()
41 | version getVersionName()
42 | artifact("$buildDir/outputs/aar/${getArtificatId()}-release.aar")
43 | }
44 | }
45 |
46 | repositories {
47 | maven {
48 | name = "GitHubPackages"
49 | url = uri("https://maven.pkg.github.com/trustwallet/trust-web3-provider")
50 | credentials {
51 | username = System.getenv("GPR_USER")
52 | password = System.getenv("GPR_API_KEY")
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/android/lib/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/android/lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':lib'
3 | rootProject.name = "Web3 Provider Demo"
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trustwallet/trust-web3-provider/e94d1c57bed40b90deedf04d3e56c8c817be9886/bun.lockb
--------------------------------------------------------------------------------
/bunfig.toml:
--------------------------------------------------------------------------------
1 | [test]
2 | root = "./packages"
--------------------------------------------------------------------------------
/docs/BUILD.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | ### Fork and set up your local env
4 |
5 | Install bun
6 |
7 | ```bash
8 | curl https://bun.sh/install | bash
9 | ```
10 |
11 | Install deps
12 |
13 | ```bash
14 | bun install
15 | ```
16 |
17 | ### Useful scripts
18 |
19 | Build packages
20 |
21 | ```bash
22 | bun run build:packages
23 | ```
24 |
25 | Run tests
26 |
27 | ```bash
28 | bun run test
29 | ```
30 |
31 | Run tests watch mode
32 |
33 | ```bash
34 | bun run test:watch
35 | ```
36 |
37 | ### Test locally
38 |
39 | If you wish to test a package inside the extension, let's say solana:
40 |
41 | ```bash
42 | cd packages/solana
43 | npm link
44 | ```
45 |
46 | Then you can run in the extension workspace:
47 |
48 | ```bash
49 | npm link @trustwallet/web3-provider-solana
50 | ```
51 |
52 | ---
53 |
54 | ## Adding a new chain
55 |
56 | [Adding a new chain](/docs/NEW.md)
57 |
--------------------------------------------------------------------------------
/docs/NEW.md:
--------------------------------------------------------------------------------
1 | ## Adding a new chain
2 |
3 | Run the generate command
4 |
5 | ```bash
6 | bun run generate awesomeNewChain
7 | ```
8 |
9 | This will generate all the boilerplate structure for your new chain:
10 |
11 | ```
12 | 📦 newChain
13 | ├─ tests
14 | ├─ exceptions
15 | ├─ types
16 | ├─ index.ts
17 | ├─ NewChainProvider.ts
18 | └─ package.json
19 | ```
20 |
21 | Then you can build it using the command
22 |
23 | ```bash
24 | bun run build:packages
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/USAGE.md:
--------------------------------------------------------------------------------
1 | ## Using the library
2 |
3 | # 1. Run
4 |
5 | ```bash
6 | npm i @trustwallet/web3-provider-core
7 | ```
8 |
9 | # 2. Install your desired chain
10 |
11 | Let's use for example, ethereum
12 |
13 | ```bash
14 | npm i @trustwallet/web3-provider-ethereum
15 | ```
16 |
17 | # 3. Example usage
18 |
19 | ```typescript
20 | import { Web3Provider } from '@trustwallet/web3-provider-core';
21 | import { EthereumProvider } from '@trustwallet/web3-provider-ethereum';
22 |
23 | const ethereum = new EthereumProvider();
24 |
25 | new Web3Provider({
26 | strategy: AdapterStrategy.PROMISES,
27 | handler: (params: IHandlerParams) => {},
28 | }).registerProvider(ethereum);
29 |
30 | // Register the ethereum provider
31 | window.ethereum = ethereum;
32 | ```
33 |
34 | dApps that use EIP-1193 will be able to connect to your wallet now.
35 |
36 | ---
37 |
38 | ## Find more about the chains
39 |
40 | - Cosmos [Docs](/packages/cosmos/README.md)
41 | - Solana - _Wallet Standard fully compatible_ [Docs](/packages/solana/README.md)
42 | - Ethereum _EIP-1193_ [Docs](/packages/ethereum/README.md)
43 |
44 | ### Using the callback Adapter
45 |
46 | ```typescript
47 | const provider = new Web3Provider({
48 | strategy: AdapterStrategy.CALLBACK,
49 | }).registerProvider(ethereum);
50 |
51 | const handler = (params: IHandlerParams) => {
52 | provider.sendResponse(params.id, ['0x0....']);
53 | };
54 |
55 | provider.setHandler(handler);
56 | ```
57 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import globals from 'globals';
2 | import tseslint from 'typescript-eslint';
3 |
4 | export default [
5 | { languageOptions: { globals: globals.browser } },
6 | ...tseslint.configs.recommended,
7 | {
8 | ignores: ['templates/', '**/dist'],
9 | },
10 | {
11 | rules: {
12 | 'no-unused-vars': 'off',
13 | '@typescript-eslint/no-unused-vars': 'off',
14 | '@typescript-eslint/no-explicit-any': 'off',
15 | '@typescript-eslint/no-namespace': 'off',
16 | },
17 | },
18 | ];
19 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | before_install:
2 | - echo "installing"
3 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider",
3 | "private": true,
4 | "module": "index.ts",
5 | "type": "module",
6 | "scripts": {
7 | "lint": "eslint .",
8 | "build": "bun build --entrypoints ./index.ts --outdir ./dist --target browser",
9 | "test:watch": "bun test --watch --only",
10 | "test": "bun test",
11 | "test:only": "bun run test --only",
12 | "dev": "bun run ./scripts/link.ts",
13 | "build:packages": "bun run ./scripts/build.ts",
14 | "generate": "bun run ./scripts/generate.ts",
15 | "rename": "bun run ./scripts/rename.ts",
16 | "publish": "bun run ./scripts/publish.ts",
17 | "build:version": "NODE_ENV=production semantic-release --dry-run"
18 | },
19 | "workspaces": [
20 | "packages/*"
21 | ],
22 | "devDependencies": {
23 | "@rollup/plugin-commonjs": "22.0.2",
24 | "@rollup/plugin-json": "6.1.0",
25 | "@rollup/plugin-node-resolve": "13.3.0",
26 | "@types/bun": "latest",
27 | "esbuild": "0.17.19",
28 | "eslint": "9.x",
29 | "globals": "^15.3.0",
30 | "rollup": "2.79.2",
31 | "rollup-plugin-esbuild": "4.9.3",
32 | "simple-scaffold": "2.2.1",
33 | "typescript": "^5.0.0",
34 | "typescript-eslint": "^7.10.0"
35 | },
36 | "peerDependencies": {
37 | "typescript": "^5.0.0"
38 | },
39 | "dependencies": {
40 | "semantic-release-export-data": "^1.1.0",
41 | "semantic-release": "^24.2.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/packages/android-web3-provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/android-web3-provider",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js && rollup --config ./rollup.config2.js",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "@trustwallet/web3-provider-aptos": "workspace:*",
23 | "@trustwallet/web3-provider-core": "workspace:*",
24 | "@trustwallet/web3-provider-cosmos": "workspace:*",
25 | "@trustwallet/web3-provider-ethereum": "workspace:*",
26 | "@trustwallet/web3-provider-solana": "workspace:*",
27 | "@trustwallet/web3-provider-ton": "workspace:*",
28 | "core-js": "^3.38.1",
29 | "uuid": "9.0.1"
30 | },
31 | "devDependencies": {
32 | "@babel/preset-env": "^7.25.8",
33 | "@babel/preset-typescript": "^7.25.7",
34 | "@rollup/plugin-babel": "^6.0.4",
35 | "@rollup/plugin-terser": "^0.4.4",
36 | "@types/uuid": "^9.0.8",
37 | "rollup-plugin-corejs": "^1.0.0",
38 | "rollup-plugin-polyfill-node": "0.13.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/android-web3-provider/rollup.config.js:
--------------------------------------------------------------------------------
1 | import commonjs from '@rollup/plugin-commonjs';
2 | import { name, dependencies } from './package.json';
3 | import nodePolyfills from 'rollup-plugin-polyfill-node';
4 | import inject from '@rollup/plugin-inject';
5 | import babel from '@rollup/plugin-babel';
6 | import resolve from '@rollup/plugin-node-resolve';
7 | import json from '@rollup/plugin-json';
8 |
9 | const input = './src/index.ts';
10 | const plugins = [
11 | json(),
12 | nodePolyfills(),
13 | resolve({ browser: true, preferBuiltins: false }),
14 | commonjs(),
15 | babel({
16 | babelHelpers: 'bundled',
17 | extensions: ['.js', '.ts'],
18 | exclude: 'node_modules/**',
19 | presets: [
20 | [
21 | '@babel/preset-env',
22 | {
23 | targets: 'chrome 67',
24 | corejs: '3.21.1',
25 | },
26 | ],
27 | '@babel/preset-typescript',
28 | ],
29 | }),
30 | inject({
31 | modules: {
32 | Buffer: ['buffer', 'Buffer'],
33 | },
34 | }),
35 | ];
36 |
37 | function createConfig(packageName) {
38 | return [
39 | {
40 | input,
41 | plugins,
42 | output: {
43 | file: './dist/index.js',
44 | format: 'umd',
45 | name: packageName,
46 | sourcemap: false,
47 | },
48 | },
49 | ];
50 | }
51 |
52 | export default createConfig(name, Object.keys(dependencies));
53 |
--------------------------------------------------------------------------------
/packages/android-web3-provider/rollup.config2.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import babel from '@rollup/plugin-babel';
3 | import terser from '@rollup/plugin-terser';
4 |
5 | const input = './dist/index.js';
6 |
7 | const plugins = [
8 | babel({
9 | babelHelpers: 'bundled',
10 | extensions: ['.js'],
11 | presets: [
12 | [
13 | '@babel/preset-env',
14 | {
15 | targets: 'chrome 67',
16 | useBuiltIns: false,
17 | corejs: '3.21.1',
18 | },
19 | ],
20 | ],
21 | }),
22 |
23 | terser(),
24 | ];
25 |
26 | function createConfig(packageName) {
27 | return [
28 | {
29 | input,
30 | plugins,
31 | output: {
32 | file: '../../android/lib/src/main/res/raw/trust_min.js',
33 | format: 'umd',
34 | name: packageName,
35 | sourcemap: false,
36 | extend: true,
37 | },
38 | },
39 | ];
40 | }
41 |
42 | export default createConfig(name, Object.keys(dependencies));
43 |
--------------------------------------------------------------------------------
/packages/android-web3-provider/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':lib'
3 | rootProject.name = "Web3 Provider Demo"
--------------------------------------------------------------------------------
/packages/android-web3-provider/src/index.ts:
--------------------------------------------------------------------------------
1 | import 'core-js';
2 | import { v4 as uuidv4 } from 'uuid';
3 |
4 | import { SolanaProvider } from '@trustwallet/web3-provider-solana';
5 | import { EthereumProvider } from '@trustwallet/web3-provider-ethereum';
6 | import { CosmosProvider } from '@trustwallet/web3-provider-cosmos';
7 | import { Web3Provider } from '@trustwallet/web3-provider-core';
8 | import { AptosProvider } from '@trustwallet/web3-provider-aptos';
9 | import { TonBridge, TonProvider } from '@trustwallet/web3-provider-ton';
10 |
11 | import type {
12 | AdapterStrategyType,
13 | IHandler,
14 | } from '@trustwallet/web3-provider-core/adapter/Adapter';
15 |
16 | import type { ISolanaProviderConfig } from '@trustwallet/web3-provider-solana/types/SolanaProvider';
17 | import type { ICosmosProviderConfig } from '@trustwallet/web3-provider-cosmos/types/CosmosProvider';
18 | import type { IEthereumProviderConfig } from '@trustwallet/web3-provider-ethereum/types/EthereumProvider';
19 | import type { IAptosProviderConfig } from '@trustwallet/web3-provider-aptos/types/AptosProvider';
20 |
21 | import type { ITonProviderConfig } from '@trustwallet/web3-provider-ton/types/TonProvider';
22 | import type { ITonBridgeConfig } from '@trustwallet/web3-provider-ton/types/TonBridge';
23 |
24 | const core = (strategy: AdapterStrategyType, handler?: IHandler) =>
25 | new Web3Provider({ strategy, handler });
26 |
27 | const solana = (config: ISolanaProviderConfig) => new SolanaProvider(config);
28 |
29 | const cosmos = (config: ICosmosProviderConfig) => new CosmosProvider(config);
30 |
31 | const ethereum = (config: IEthereumProviderConfig) =>
32 | new EthereumProvider(config);
33 |
34 | const aptos = (config: IAptosProviderConfig) => new AptosProvider(config);
35 |
36 | const ton = (config: ITonProviderConfig) => new TonProvider(config);
37 |
38 | const tonBridge = (config: ITonBridgeConfig, provider: TonProvider) =>
39 | new TonBridge(config, provider);
40 |
41 | window.trustwallet = {
42 | core,
43 | solana,
44 | cosmos,
45 | ethereum,
46 | aptos,
47 | ton,
48 | tonBridge,
49 | randomUUID: () => uuidv4(),
50 | };
51 |
--------------------------------------------------------------------------------
/packages/android-web3-provider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "package.json",
4 | "rollup.config.js",
5 | "tsconfig.json",
6 | "tests",
7 | "./dist"
8 | ],
9 | "compilerOptions": {
10 | "rootDir": "./",
11 | "outDir": "./dist/types",
12 | "lib": ["ES2020", "DOM"],
13 | "target": "ES6",
14 | "module": "ESNext",
15 | "moduleResolution": "node",
16 | "esModuleInterop": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["./**/*.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/aptos/AptosProvider.ts:
--------------------------------------------------------------------------------
1 | import { BaseProvider } from '@trustwallet/web3-provider-core';
2 | import type IAptosProvider from './types/AptosProvider';
3 | import type {
4 | IAptosProviderConfig,
5 | ISignMessagePayload,
6 | } from './types/AptosProvider';
7 |
8 | export class AptosProvider extends BaseProvider implements IAptosProvider {
9 | static NETWORK = 'aptos';
10 |
11 | private _isConnected = false;
12 |
13 | private _network;
14 |
15 | public chainId: string | null = null;
16 |
17 | public address: string | null = null;
18 |
19 | static bufferToHex(buffer: Buffer | Uint8Array | string) {
20 | return '0x' + Buffer.from(buffer).toString('hex');
21 | }
22 |
23 | static messageToBuffer(message: string | Buffer) {
24 | let buffer = Buffer.from([]);
25 | try {
26 | if (typeof message === 'string') {
27 | buffer = Buffer.from(message.replace('0x', ''), 'hex');
28 | } else {
29 | buffer = Buffer.from(message);
30 | }
31 | } catch (err) {
32 | console.log(`messageToBuffer error: ${err}`);
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | constructor(config?: IAptosProviderConfig) {
39 | super();
40 |
41 | if (config) {
42 | if (config.network) {
43 | this._network = config.network;
44 |
45 | if (config.chainId) {
46 | this.chainId = config.chainId;
47 | }
48 | }
49 | }
50 | }
51 |
52 | setConfig(config: { network: string; address: string; chainId: string }) {
53 | this._network = config.network;
54 | this.address = config.address;
55 | this.chainId = config.chainId;
56 | }
57 |
58 | async connect() {
59 | const accountInfo = await this.account();
60 | this._isConnected = true;
61 | this.emit('connect');
62 | return accountInfo;
63 | }
64 |
65 | disconnect() {
66 | this._isConnected = false;
67 | this.emit('disconnect');
68 | }
69 |
70 | isConnected() {
71 | return this._isConnected;
72 | }
73 |
74 | async account() {
75 | const data = await this.request({
76 | method: 'requestAccounts',
77 | params: {},
78 | });
79 | return JSON.parse(data);
80 | }
81 |
82 | network() {
83 | return this._network;
84 | }
85 |
86 | getNetwork(): string {
87 | return AptosProvider.NETWORK;
88 | }
89 |
90 | async signMessage(payload: ISignMessagePayload) {
91 | const prefix = 'APTOS';
92 | const address = (await this.account()).address;
93 |
94 | let fullMessage = prefix;
95 |
96 | const application =
97 | window.location.protocol + '//' + window.location.hostname;
98 |
99 | if (payload.address) {
100 | fullMessage += '\naddress: ' + address;
101 | }
102 |
103 | if (payload.application) {
104 | fullMessage += '\napplication: ' + application;
105 | }
106 |
107 | if (payload.chainId) {
108 | fullMessage += '\nchainId: ' + this.chainId;
109 | }
110 |
111 | fullMessage += '\nmessage: ' + payload.message;
112 | fullMessage += '\nnonce: ' + payload.nonce;
113 |
114 | const buffer = Buffer.from(fullMessage);
115 | const hex = AptosProvider.bufferToHex(buffer);
116 |
117 | return this.request({ method: 'signMessage', params: { data: hex } }).then(
118 | (signature) => {
119 | return {
120 | address: address,
121 | application: application,
122 | chainId: this.chainId,
123 | fullMessage: fullMessage,
124 | message: payload.message,
125 | nonce: payload.nonce,
126 | prefix: prefix,
127 | signature: signature,
128 | };
129 | },
130 | );
131 | }
132 |
133 | async signAndSubmitTransaction(tx: string) {
134 | const signedTx = await this.signTransaction(tx);
135 |
136 | const hex = await this.request({
137 | method: 'sendTransaction',
138 | params: { tx: signedTx },
139 | });
140 |
141 | return { hash: AptosProvider.messageToBuffer(hex).toString() };
142 | }
143 |
144 | async signTransaction(tx: string) {
145 | const hex = await this.request({
146 | method: 'signTransaction',
147 | params: { data: tx },
148 | });
149 |
150 | return JSON.parse(AptosProvider.messageToBuffer(hex).toString());
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/packages/aptos/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 |
5 |
6 | ,---. |
7 | |---|,---.|--- ,---.,---.
8 | | || || | |`---.
9 | ` '|---'`--- `---'`---'
10 | |
11 |
12 | ```
13 |
14 | ### Aptos JavaScript Provider Implementation
15 |
16 | ### Config Object
17 |
18 | ```typescript
19 | const config: {
20 | isTrust?: boolean;
21 | network?: string;
22 | chainId?: string | null;
23 | } = {};
24 | ```
25 |
26 | ### Usage
27 |
28 | ```typescript
29 | const aptos = new AptosProvider(config);
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/aptos/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/aptos/index.ts:
--------------------------------------------------------------------------------
1 | export * from './AptosProvider';
2 |
--------------------------------------------------------------------------------
/packages/aptos/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-aptos",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": { },
22 | "devDependencies": {
23 | "@trustwallet/web3-provider-core": "workspace:*"
24 | }
25 | }
--------------------------------------------------------------------------------
/packages/aptos/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/aptos/tests/AptosProvider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { AptosProvider } from '../AptosProvider';
4 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
5 |
6 | let aptos = new AptosProvider();
7 | const account = '0x0000000000000000000000000000000000000000';
8 |
9 | afterEach(() => {
10 | aptos = new AptosProvider();
11 | });
12 |
13 | (global as any).window = {};
14 |
15 | // Mock window
16 | Object.assign(global.window, {
17 | location: {
18 | protocol: 'https',
19 | hostname: 'trust',
20 | },
21 | });
22 |
23 | test('Aptos -> connect()', async () => {
24 | new Web3Provider({
25 | strategy: AdapterStrategy.PROMISES,
26 | handler: () => Promise.resolve(JSON.stringify([account])),
27 | }).registerProvider(aptos);
28 |
29 | const accounts = await aptos.connect();
30 | expect(accounts).toEqual([account]);
31 | });
32 |
33 | test('Aptos -> signMessage()', async () => {
34 | const handler = jest.fn(() => Promise.resolve(JSON.stringify([account])));
35 |
36 | new Web3Provider({
37 | strategy: AdapterStrategy.PROMISES,
38 | handler,
39 | }).registerProvider(aptos);
40 |
41 | aptos.setConfig({
42 | network: 'n',
43 | address: '123',
44 | chainId: '1',
45 | });
46 |
47 | await aptos.signMessage({
48 | address: '1',
49 | application: '2',
50 | message: '3',
51 | nonce: '4',
52 | chainId: '1',
53 | });
54 |
55 | expect(handler).toBeCalledTimes(2);
56 | expect(handler).toHaveBeenNthCalledWith(
57 | 2,
58 | expect.objectContaining({
59 | name: 'signMessage',
60 | }),
61 | );
62 | });
63 |
--------------------------------------------------------------------------------
/packages/aptos/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/aptos/types/AptosProvider.ts:
--------------------------------------------------------------------------------
1 | export interface IAptosProviderConfig {
2 | isTrust?: boolean;
3 | network?: string;
4 | chainId?: string | null;
5 | }
6 |
7 | export default interface IAptosProvider {}
8 |
9 | export interface ISignMessagePayload {
10 | address: string;
11 | application: string;
12 | message: string;
13 | nonce: string;
14 | chainId: string;
15 | }
16 |
--------------------------------------------------------------------------------
/packages/core/Provider.ts:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events';
2 | import type { CallbackAdapter } from './adapter/CallbackAdapter';
3 | import type { PromiseAdapter } from './adapter/PromiseAdapter';
4 | import { Adapter, AdapterStrategy } from './adapter/Adapter';
5 |
6 | export interface IRequestArguments {
7 | method: string;
8 | params?: unknown[] | object;
9 | }
10 |
11 | export interface IBaseProvider {
12 | sendResponse(requestId: number, response: any): void;
13 | sendError(requestId: number, response: any): void;
14 | setAdapter(adapter: Adapter): IBaseProvider;
15 | request(args: IRequestArguments): Promise;
16 | }
17 |
18 | /**
19 | * Base provider
20 | *
21 | * All providers should extend this one
22 | */
23 | export abstract class BaseProvider
24 | extends EventEmitter
25 | implements IBaseProvider
26 | {
27 | adapter!: CallbackAdapter | PromiseAdapter;
28 |
29 | setAdapter(adapter: CallbackAdapter | PromiseAdapter) {
30 | this.adapter = adapter;
31 | return this;
32 | }
33 |
34 | /**
35 | *
36 | * @param args
37 | */
38 | async request(args: IRequestArguments): Promise {
39 | try {
40 | if (!this.adapter) {
41 | throw new Error(
42 | 'No adapter set, maybe you forgot to register the provider?',
43 | );
44 | }
45 |
46 | const res = await this.adapter.request(args, this.getNetwork());
47 |
48 | // Emit internally the response
49 | this.emit('onResponseReady', args, res);
50 |
51 | return res as T;
52 | } catch (e) {
53 | throw e;
54 | }
55 | }
56 |
57 | abstract getNetwork(): string;
58 |
59 | /**
60 | * Send Response if the adapter is on callback mode
61 | * @param requestId
62 | * @param response
63 | */
64 | sendResponse(requestId: number, response: any) {
65 | if (!this.adapter) {
66 | throw new Error('Adapter not found');
67 | }
68 |
69 | if (this.adapter.getStrategy() !== AdapterStrategy.CALLBACK) {
70 | throw new Error('Trying to send callback request on promisified adapter');
71 | }
72 |
73 | (this.adapter as CallbackAdapter).sendResponse(requestId, response);
74 | }
75 |
76 | /**
77 | * Send error
78 | * @param requestId
79 | * @param response
80 | */
81 | sendError(requestId: number, response: any) {
82 | if (!this.adapter) {
83 | throw new Error('Adapter not found');
84 | }
85 |
86 | if (this.adapter.getStrategy() !== AdapterStrategy.CALLBACK) {
87 | throw new Error('Trying to send callback request on promisified adapter');
88 | }
89 |
90 | (this.adapter as CallbackAdapter).sendError(requestId, response);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 |
5 | // __ __ __ ___
6 | // / ` / \ |__) |__
7 | // \__, \__/ | \ |___
8 | //
9 |
10 | ```
11 |
12 | ### Core Package Description
13 |
14 | Handler strategy and handler registration
15 |
--------------------------------------------------------------------------------
/packages/core/adapter/Adapter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | IHandlerParams,
3 | ICallbackAdapterRequestParams,
4 | } from './CallbackAdapter';
5 |
6 | export const AdapterStrategy = {
7 | PROMISES: 'PROMISES',
8 | CALLBACK: 'CALLBACK',
9 | } as const;
10 |
11 | interface IAdapter {}
12 |
13 | export interface IAdapterRequestParams {
14 | params?: unknown[] | object;
15 | method: string;
16 | }
17 |
18 | export type IHandler = (params: IHandlerParams) => Promise | void;
19 |
20 | export type AdapterStrategyType = keyof typeof AdapterStrategy;
21 |
22 | /**
23 | * Abstract adapter
24 | */
25 | export abstract class Adapter {
26 | /**
27 | * Strategy to wait for wallet's response
28 | */
29 | private strategy!: AdapterStrategyType;
30 |
31 | /**
32 | * Function that will handle requests on wallet side
33 | */
34 | private handler!: IHandler;
35 |
36 | static isCallbackAdapterRequest(
37 | params: IAdapterRequestParams | ICallbackAdapterRequestParams,
38 | ): params is ICallbackAdapterRequestParams {
39 | return (params as ICallbackAdapterRequestParams).id !== undefined;
40 | }
41 |
42 | constructor(strategy: AdapterStrategyType) {
43 | this.setStrategy(strategy);
44 | }
45 |
46 | setHandler(remoteHandler: IHandler) {
47 | this.handler = remoteHandler;
48 | return this;
49 | }
50 |
51 | request(
52 | params: IAdapterRequestParams | ICallbackAdapterRequestParams,
53 | network: string,
54 | ): Promise | void {
55 | if (!this.handler) {
56 | throw new Error('No handler defined for Adapter');
57 | }
58 |
59 | if (Adapter.isCallbackAdapterRequest(params)) {
60 | return this.handler({
61 | network,
62 | id: params.id,
63 | name: params.method,
64 | params: params.params,
65 | object: params.params,
66 | });
67 | }
68 |
69 | return this.handler({
70 | name: params.method,
71 | network,
72 | params: params.params,
73 | object: params.params,
74 | });
75 | }
76 |
77 | setStrategy(strategy: AdapterStrategyType) {
78 | this.strategy = strategy;
79 | return this;
80 | }
81 |
82 | getStrategy() {
83 | return this.strategy;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/packages/core/adapter/CallbackAdapter.ts:
--------------------------------------------------------------------------------
1 | import { RPCError } from '../exceptions/RPCError';
2 | import { IRequestArguments } from '../Provider';
3 |
4 | import { Adapter, AdapterStrategy, IAdapterRequestParams } from './Adapter';
5 |
6 | export interface ICallbackAdapterRequestParams extends IAdapterRequestParams {
7 | id: number;
8 | }
9 |
10 | export interface IHandlerParams {
11 | id?: ICallbackAdapterRequestParams['id'];
12 | network: string;
13 | name: IAdapterRequestParams['method'];
14 | params: IAdapterRequestParams['params'];
15 |
16 | // Retro compatibility with apps
17 | object: IAdapterRequestParams['params'];
18 | }
19 |
20 | interface ICallbackEntry {
21 | resolve: (value: unknown) => void;
22 | reject: (error?: any) => any;
23 | }
24 |
25 | /**
26 | * CallbackAdapter
27 | *
28 | * Adapter implementation that uses callbacks and requires
29 | * sendResponse() to resolve the web3 promise or sendError() to reject it
30 | */
31 | export class CallbackAdapter extends Adapter {
32 | constructor() {
33 | super(AdapterStrategy.CALLBACK);
34 | }
35 |
36 | private callback: Map = new Map();
37 |
38 | async request(params: IRequestArguments, network: string): Promise {
39 | return new Promise((resolve, reject) => {
40 | const id = new Date().getTime() + Math.floor(Math.random() * 1000);
41 | this.callback.set(id.toString(), { reject, resolve });
42 | super.request({ ...params, id }, network);
43 | });
44 | }
45 |
46 | public sendResponse(requestId: number, response: any) {
47 | if (this.callback.has(requestId.toString())) {
48 | const callback = this.callback.get(requestId.toString());
49 | this.callback.delete(requestId.toString());
50 | callback?.resolve(response);
51 | } else {
52 | console.error(`Unable to find callback for requestId: ${requestId}`);
53 | }
54 | }
55 |
56 | public sendError(requestId: number, error: any) {
57 | if (this.callback.has(requestId.toString())) {
58 | const callback = this.callback.get(requestId.toString());
59 | this.callback.delete(requestId.toString());
60 |
61 | let errorParsed = error;
62 |
63 | // Parse error strings from mobile
64 | if (
65 | typeof errorParsed === 'string' &&
66 | !isNaN(parseInt(errorParsed, 10))
67 | ) {
68 | errorParsed = new RPCError(parseInt(errorParsed, 10), errorParsed);
69 | }
70 |
71 | callback?.reject(errorParsed);
72 | } else {
73 | console.error(`Unable to find callback for requestId: ${requestId}`);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/packages/core/adapter/PromiseAdapter.ts:
--------------------------------------------------------------------------------
1 | import { IRequestArguments } from '../Provider';
2 | import { Adapter, AdapterStrategy, IAdapterRequestParams } from './Adapter';
3 |
4 | export class PromiseAdapter extends Adapter {
5 | constructor() {
6 | super(AdapterStrategy.PROMISES);
7 | }
8 |
9 | request(params: IRequestArguments, network: string): Promise | void {
10 | return super.request(params as IAdapterRequestParams, network);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/core/adapter/tests/CallbackBridge.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, jest } from 'bun:test';
2 | import { CallbackAdapter, IHandlerParams } from '../CallbackAdapter';
3 | import { AdapterStrategy } from '../Adapter';
4 |
5 | test('CallbackAdapter → Strategy is correct', () => {
6 | const adapter = new CallbackAdapter();
7 | expect(adapter.getStrategy()).toEqual(AdapterStrategy.CALLBACK);
8 | });
9 |
10 | test('CallbackAdapter → Handler is set and resolved promise', async () => {
11 | const adapter = new CallbackAdapter();
12 |
13 | const response = 'pong';
14 |
15 | const handler = jest.fn((params: IHandlerParams) => {
16 | adapter.sendResponse(params.id!, response);
17 | return Promise.resolve();
18 | });
19 |
20 | adapter.setHandler(handler);
21 |
22 | const params = { method: 'ping', params: [] };
23 |
24 | const promise = adapter.request(params, 'ethereum');
25 |
26 | expect(handler).toHaveBeenCalled();
27 | expect(promise).resolves.toBe(response);
28 | });
29 |
30 | test('CallbackAdapter → Handler is set and rejects promise', async () => {
31 | const adapter = new CallbackAdapter();
32 |
33 | const response = 'error';
34 |
35 | const handler = jest.fn((params: IHandlerParams) => {
36 | adapter.sendError(params.id!, response);
37 | return Promise.resolve();
38 | });
39 |
40 | adapter.setHandler(handler);
41 |
42 | const params = { method: 'ping', params: [] };
43 |
44 | const promise = adapter.request(params, 'ethereum');
45 |
46 | expect(handler).toHaveBeenCalled();
47 | expect(promise).rejects.toBe(response);
48 | });
49 |
50 | test('CallbackAdapter → Parameters are correct', async () => {
51 | const adapter = new CallbackAdapter();
52 |
53 | const response = 'pong';
54 |
55 | const handler = jest.fn((params: IHandlerParams) => {
56 | adapter.sendResponse(params.id!, response);
57 | return Promise.resolve();
58 | });
59 |
60 | adapter.setHandler(handler);
61 |
62 | const params = { method: 'ping', params: [1, 2, 3] };
63 | adapter.request(params, 'ethereum');
64 |
65 | expect(handler).toHaveBeenCalledWith(
66 | expect.objectContaining({
67 | name: params.method,
68 | params: params.params,
69 | network: 'ethereum',
70 | }),
71 | );
72 | });
73 |
--------------------------------------------------------------------------------
/packages/core/adapter/tests/PromiseBridge.spec.ts:
--------------------------------------------------------------------------------
1 | import { expect, test, jest } from 'bun:test';
2 | import { PromiseAdapter } from '../PromiseAdapter';
3 | import { AdapterStrategy, IAdapterRequestParams } from '../Adapter';
4 | import { IHandlerParams } from '../CallbackAdapter';
5 |
6 | test('PromiseAdapter → Strategy is correct', () => {
7 | const adapter = new PromiseAdapter();
8 | expect(adapter.getStrategy()).toEqual(AdapterStrategy.PROMISES);
9 | });
10 |
11 | test('PromiseAdapter → Handler is set and resolved promise', async () => {
12 | const adapter = new PromiseAdapter();
13 | const response = 'pong';
14 |
15 | const handler = jest.fn((_params: IHandlerParams) => {
16 | return Promise.resolve(response);
17 | });
18 |
19 | adapter.setHandler(handler);
20 |
21 | const params = { method: 'ping', params: [] };
22 | const promise = adapter.request(params, 'ethereum');
23 |
24 | expect(handler).toHaveBeenCalled();
25 | expect(promise).resolves.toBe(response);
26 | });
27 |
28 | test('PromiseAdapter → Handler is set and rejects promise', async () => {
29 | const adapter = new PromiseAdapter();
30 |
31 | const response = 'error';
32 |
33 | const handler = jest.fn((_params: IHandlerParams) => {
34 | return Promise.reject(response);
35 | });
36 |
37 | adapter.setHandler(handler);
38 |
39 | const params = { method: 'ping', params: [] };
40 |
41 | const promise = adapter.request(params, 'ethereum');
42 |
43 | expect(handler).toHaveBeenCalled();
44 | expect(promise).rejects.toBe(response);
45 | });
46 |
47 | test('PromiseAdapter → Parameters are correct', async () => {
48 | const adapter = new PromiseAdapter();
49 |
50 | const handler = jest.fn((_params: IHandlerParams) => {
51 | return Promise.resolve();
52 | });
53 |
54 | adapter.setHandler(handler);
55 |
56 | const params = { method: 'ping', params: [1, 2, 3] };
57 | adapter.request(params, 'ethereum');
58 |
59 | expect(handler).toHaveBeenCalledWith(
60 | expect.objectContaining({
61 | name: params.method,
62 | params: params.params,
63 | network: 'ethereum',
64 | }),
65 | );
66 | });
67 |
--------------------------------------------------------------------------------
/packages/core/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/core/index.ts:
--------------------------------------------------------------------------------
1 | import { BaseProvider, IBaseProvider } from './Provider';
2 | import {
3 | AdapterStrategy,
4 | AdapterStrategyType,
5 | IHandler,
6 | type Adapter,
7 | } from './adapter/Adapter';
8 | import { CallbackAdapter } from './adapter/CallbackAdapter';
9 | import { PromiseAdapter } from './adapter/PromiseAdapter';
10 |
11 | /**
12 | * Trust web3 Provider
13 | *
14 | *
15 | */
16 | export class Web3Provider {
17 | private adapter!: Adapter;
18 |
19 | constructor(params: { strategy: AdapterStrategyType; handler?: IHandler }) {
20 | const adapter =
21 | params.strategy === AdapterStrategy.CALLBACK
22 | ? new CallbackAdapter()
23 | : new PromiseAdapter();
24 |
25 | if (params.handler) {
26 | adapter.setHandler(params.handler);
27 | }
28 |
29 | this.setAdapter(adapter);
30 | }
31 |
32 | setHandler(handler: IHandler) {
33 | this.adapter.setHandler(handler);
34 | }
35 |
36 | private setAdapter(adapter: Adapter) {
37 | this.adapter = adapter;
38 | return this;
39 | }
40 |
41 | registerProvider(provider: BaseProvider) {
42 | provider.setAdapter(this.adapter);
43 | return this;
44 | }
45 |
46 | registerProviders(providers: BaseProvider[]) {
47 | providers.forEach((provider) => this.registerProvider(provider));
48 | return this;
49 | }
50 |
51 | sendResponse(requestId: number, response: any) {
52 | if (this.adapter.getStrategy() === 'CALLBACK') {
53 | (this.adapter as CallbackAdapter).sendResponse(requestId, response);
54 | }
55 | }
56 |
57 | sendError(requestId: number, error: any) {
58 | if (this.adapter.getStrategy() === 'CALLBACK') {
59 | (this.adapter as CallbackAdapter).sendError(requestId, error);
60 | }
61 | }
62 | }
63 |
64 | export * from './Provider';
65 |
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-core",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build:clean; rollup -w --config ./rollup.config.js",
20 | "build": "bun build ./index.ts --outdir ./dist"
21 | },
22 | "dependencies": {
23 | "events": "^3.3.0",
24 | "uuid": "9.0.1"
25 | },
26 | "devDependencies": {
27 | "@types/uuid": "^9.0.8"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/core/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": [
4 | "package.json",
5 | "rollup.config.js",
6 | "tsconfig.json",
7 | "tests",
8 | "./dist",
9 | "**/*.spec.ts"
10 | ],
11 | "compilerOptions": {
12 | "rootDir": "./",
13 | "outDir": "./dist/types",
14 | "emitDeclarationOnly": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/cosmos/CosmosProvider.ts:
--------------------------------------------------------------------------------
1 | import {
2 | BaseProvider,
3 | IRequestArguments,
4 | } from '@trustwallet/web3-provider-core';
5 | import {
6 | BroadcastMode,
7 | DirectSignDoc,
8 | ICosmosProvider,
9 | ICosmosProviderConfig,
10 | } from './types/CosmosProvider';
11 | import { StdSignDoc } from '@cosmjs/amino';
12 | import { MobileAdapter } from './MobileAdapter';
13 | import { WalletAccount } from '@cosmos-kit/core';
14 |
15 | export class CosmosProvider extends BaseProvider implements ICosmosProvider {
16 | static NETWORK = 'cosmos';
17 |
18 | private mobileAdapter!: MobileAdapter;
19 |
20 | #disableMobileAdapter: boolean = false;
21 |
22 | /**
23 | * Mobile required overwriting this to true
24 | */
25 | isKeplr: boolean = true;
26 |
27 | isTrust: boolean = true;
28 |
29 | isTrustWallet: boolean = true;
30 |
31 | constructor(config?: ICosmosProviderConfig) {
32 | super();
33 |
34 | if (config) {
35 | if (typeof config.disableMobileAdapter !== 'undefined') {
36 | this.#disableMobileAdapter = config.disableMobileAdapter;
37 | }
38 |
39 | if (typeof config.isKeplr !== 'undefined') {
40 | this.isKeplr = config.isKeplr;
41 | }
42 |
43 | if (typeof config.isTrust !== 'undefined') {
44 | this.isTrust = config.isTrust;
45 | this.isTrustWallet = config.isTrust;
46 | }
47 | }
48 |
49 | if (!this.#disableMobileAdapter) {
50 | this.mobileAdapter = new MobileAdapter(this);
51 | }
52 | }
53 |
54 | static bufferToHex(buffer: Buffer | Uint8Array | string) {
55 | return '0x' + Buffer.from(buffer).toString('hex');
56 | }
57 |
58 | getNetwork(): string {
59 | return CosmosProvider.NETWORK;
60 | }
61 |
62 | isMobileAdapterEnabled() {
63 | return !this.#disableMobileAdapter;
64 | }
65 |
66 | enable(chainIds: string | string[]): Promise {
67 | return this.request({
68 | method: 'enable',
69 | params: { chainIds },
70 | });
71 | }
72 |
73 | /**
74 | * Call request handler directly
75 | * @param args
76 | * @returns
77 | */
78 | internalRequest(args: IRequestArguments): Promise {
79 | return super.request(args);
80 | }
81 |
82 | /**
83 | * request order is
84 | *
85 | * mobileAdapter (if enabled)
86 | * -----> client handler (internalRequest)
87 | *
88 | * @param args
89 | * @returns
90 | */
91 | request(args: IRequestArguments): Promise {
92 | const next = () => {
93 | return this.internalRequest(args) as Promise;
94 | };
95 |
96 | if (this.mobileAdapter) {
97 | return this.mobileAdapter.request(args, next);
98 | }
99 |
100 | return next();
101 | }
102 |
103 | getKey(chainId: string) {
104 | return this.request({
105 | method: 'getKey',
106 | params: { chainId: chainId },
107 | });
108 | }
109 |
110 | async sendTx(
111 | chainId: string,
112 | tx: Uint8Array,
113 | mode: BroadcastMode,
114 | ): Promise {
115 | const raw = Buffer.from(tx).toString('base64');
116 |
117 | const hash = await this.request({
118 | method: 'sendTx',
119 | params: {
120 | raw,
121 | chainId,
122 | mode,
123 | },
124 | });
125 |
126 | return new Uint8Array(Buffer.from(hash, 'hex'));
127 | }
128 |
129 | async signArbitrary(
130 | chainId: string,
131 | signerAddress: string,
132 | payload: string | Uint8Array,
133 | ) {
134 | const buffer = Buffer.from(payload);
135 | const data = CosmosProvider.bufferToHex(buffer);
136 |
137 | const signature = await this.request<{ signature: string }>({
138 | method: 'signArbitrary',
139 | params: {
140 | chainId,
141 | data,
142 | signerAddress,
143 | },
144 | });
145 |
146 | return signature;
147 | }
148 |
149 | async signAmino(chainId: string, _signer: string, signDoc: StdSignDoc) {
150 | const response = await this.request({
151 | method: 'signAmino',
152 | params: {
153 | chainId,
154 | sign_doc: signDoc,
155 | },
156 | });
157 |
158 | const { signed, signature } = JSON.parse(response);
159 | return { signed: signed as StdSignDoc, signature };
160 | }
161 |
162 | async signDirect(
163 | chainId: string,
164 | signerAddress: string,
165 | signDoc: DirectSignDoc,
166 | ) {
167 | const object = {
168 | bodyBytes: CosmosProvider.bufferToHex(signDoc.bodyBytes),
169 | authInfoBytes: CosmosProvider.bufferToHex(signDoc.authInfoBytes),
170 | };
171 |
172 | const response = await this.request({
173 | method: 'signDirect',
174 | params: {
175 | signerAddress,
176 | chainId,
177 | sign_doc: object,
178 | },
179 | });
180 |
181 | const { signature } = JSON.parse(response);
182 | return { signed: signDoc, signature: signature as string };
183 | }
184 |
185 | experimentalSuggestChain() {}
186 |
187 | getOfflineSignerDirect(chainId: string) {
188 | return {
189 | getAccounts: async () => {
190 | return [await this.getKey(chainId)];
191 | },
192 |
193 | signDirect: async (signerAddress: string, signDoc: DirectSignDoc) => {
194 | if (chainId !== signDoc.chainId) {
195 | throw new Error('Unmatched chain id with the offline signer');
196 | }
197 |
198 | const key = await this.getKey(signDoc.chainId);
199 |
200 | if (key.address !== signerAddress) {
201 | throw new Error('Unknown signer address');
202 | }
203 |
204 | return await this.signDirect(chainId, signerAddress, signDoc);
205 | },
206 | };
207 | }
208 |
209 | getOfflineSigner(chainId: string) {
210 | return this.getOfflineSignerAmino(chainId);
211 | }
212 |
213 | getOfflineSignerAuto(chainId: string) {
214 | return this.getOfflineSignerAmino(chainId);
215 | }
216 |
217 | getOfflineSignerAmino(chainId: string) {
218 | return {
219 | getAccounts: async () => {
220 | const key = (await this.getKey(chainId)) as unknown as {
221 | pubKey: Buffer;
222 | bech32Address: string;
223 | };
224 |
225 | return [
226 | {
227 | address: key.bech32Address,
228 | algo: 'secp256k1',
229 | pubkey: key.pubKey,
230 | },
231 | ];
232 | },
233 |
234 | sign: (signerAddress: string, signDoc: StdSignDoc) => {
235 | return this.signAmino(chainId, signerAddress, signDoc);
236 | },
237 |
238 | signAmino: (signerAddress: string, signDoc: StdSignDoc) => {
239 | return this.signAmino(chainId, signerAddress, signDoc);
240 | },
241 | };
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/packages/cosmos/MobileAdapter.ts:
--------------------------------------------------------------------------------
1 | import { IRequestArguments } from '@trustwallet/web3-provider-core';
2 | import { CosmosProvider } from './CosmosProvider';
3 |
4 | /**
5 | * Adapting some requests to legacy mobile API
6 | *
7 | * This adapter provides the APIs with the method names and params the extension and mobile are
8 | * ready to handle
9 | */
10 | export class MobileAdapter {
11 | private provider!: CosmosProvider;
12 |
13 | constructor(provider: CosmosProvider) {
14 | this.provider = provider;
15 | }
16 |
17 | /**
18 | * Mobile adapter maps some cosmos methods to existing mobile method names
19 | * @param args
20 | * @param next
21 | * @returns
22 | */
23 | async request(
24 | args: IRequestArguments,
25 | next: () => Promise,
26 | ): Promise {
27 | if (args.method === 'getKey') {
28 | const res = await this.provider.internalRequest({
29 | method: 'requestAccounts',
30 | params: args.params,
31 | });
32 |
33 | const account = JSON.parse(res);
34 |
35 | return {
36 | algo: 'secp256k1',
37 | address: account.address,
38 | bech32Address: account.address,
39 | pubKey: Buffer.from(account.pubKey, 'hex'),
40 | } as unknown as T;
41 | }
42 |
43 | const map: Record = {
44 | signAmino: 'signTransaction',
45 | signDirect: 'signTransaction',
46 | signArbitrary: 'signMessage',
47 | sendTx: 'sendTransaction',
48 | };
49 |
50 | if (map[args.method]) {
51 | return this.provider.internalRequest({
52 | method: map[args.method],
53 | params: args.params,
54 | });
55 | }
56 |
57 | return next();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/cosmos/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 |
5 | // __ __ __ __ __
6 | // / ` / \ /__` |\/| / \ /__`
7 | // \__, \__/ .__/ | | \__/ .__/
8 | //
9 |
10 | ```
11 |
12 | ### Cosmos JavaScript Provider Implementation
13 |
14 | ### Config Object
15 |
16 | ```typescript
17 | const config: {
18 | disableMobileAdapter?: boolean;
19 | isKeplr?: boolean;
20 | isTrust?: boolean;
21 | } = {};
22 | ```
23 |
24 | ### Usage
25 |
26 | ```typescript
27 | const cosmos = new CosmosProvider(config);
28 | ```
29 |
--------------------------------------------------------------------------------
/packages/cosmos/index.ts:
--------------------------------------------------------------------------------
1 | export * from './CosmosProvider';
2 |
--------------------------------------------------------------------------------
/packages/cosmos/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-cosmos",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "@cosmjs/amino": "0.32.3",
23 | "@cosmjs/proto-signing": "^0.32.3",
24 | "@trustwallet/web3-provider-core": "workspace:*"
25 | },
26 | "devDependencies": {
27 | "@cosmos-kit/core": "^2.10.0"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/cosmos/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/cosmos/tests/CosmosProvider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
4 | import { IHandlerParams } from '@trustwallet/web3-provider-core/adapter/CallbackAdapter';
5 | import { SignTypedDataVersion, TypedDataUtils } from '@metamask/eth-sig-util';
6 | import { CosmosProvider } from '../CosmosProvider';
7 | import { StdSignDoc } from '@cosmjs/amino';
8 | import { DirectSignDoc } from '@cosmos-kit/core';
9 | import { BroadcastMode } from '../types/CosmosProvider';
10 |
11 | let cosmos = new CosmosProvider();
12 | const chainId = 'atom';
13 |
14 | const account = 'cosmos1utt2qsfwr7pmqjw87aaf6rpgx049zel779e5ur';
15 | const pubKey = Buffer.from('hello world').toString('hex');
16 |
17 | afterEach(() => {
18 | cosmos = new CosmosProvider();
19 | });
20 |
21 | test('Cosmos Provider → initialization config is ok', async () => {
22 | cosmos = new CosmosProvider({ isKeplr: true });
23 | expect(cosmos.isKeplr).toBeTrue();
24 |
25 | cosmos = new CosmosProvider({ isKeplr: false });
26 | expect(cosmos.isKeplr).toBeFalse();
27 | });
28 |
29 | test('Cosmos Provider → getKey -> payload is correct', async () => {
30 | const handler = jest.fn((_params: IHandlerParams) =>
31 | Promise.resolve(JSON.stringify({ address: account, pubKey })),
32 | );
33 |
34 | new Web3Provider({
35 | strategy: AdapterStrategy.PROMISES,
36 | handler,
37 | }).registerProvider(cosmos);
38 |
39 | await cosmos.getKey(chainId);
40 |
41 | expect(handler).toHaveBeenCalledWith(
42 | expect.objectContaining({
43 | network: CosmosProvider.NETWORK,
44 | params: { chainId },
45 | }),
46 | );
47 | });
48 |
49 | test('Cosmos Provider → signAmino -> payload is correct', async () => {
50 | const handler = jest.fn((_params: IHandlerParams) =>
51 | Promise.resolve(JSON.stringify({ address: account, pubKey })),
52 | );
53 |
54 | new Web3Provider({
55 | strategy: AdapterStrategy.PROMISES,
56 | handler,
57 | }).registerProvider(cosmos);
58 |
59 | await cosmos.signAmino(chainId, account, {
60 | foo: 'bar',
61 | } as unknown as StdSignDoc);
62 |
63 | expect(handler).toHaveBeenCalledWith(
64 | expect.objectContaining({
65 | network: CosmosProvider.NETWORK,
66 | name: 'signTransaction',
67 | params: { chainId, sign_doc: { foo: 'bar' } },
68 | }),
69 | );
70 | });
71 |
72 | test('Cosmos Provider → signDirect -> payload is correct', async () => {
73 | const handler = jest.fn((_params: IHandlerParams) =>
74 | Promise.resolve(JSON.stringify({ address: account, pubKey })),
75 | );
76 |
77 | new Web3Provider({
78 | strategy: AdapterStrategy.PROMISES,
79 | handler,
80 | }).registerProvider(cosmos);
81 |
82 | const payload = {
83 | bodyBytes: Buffer.from('bar'),
84 | authInfoBytes: Buffer.from('bar'),
85 | chainId,
86 | accountNumber: null,
87 | };
88 |
89 | await cosmos.signDirect(chainId, account, payload);
90 |
91 | expect(handler).toHaveBeenCalledWith(
92 | expect.objectContaining({
93 | name: 'signTransaction',
94 | network: CosmosProvider.NETWORK,
95 | params: {
96 | signerAddress: account,
97 | chainId,
98 | sign_doc: {
99 | bodyBytes: CosmosProvider.bufferToHex(payload.bodyBytes),
100 | authInfoBytes: CosmosProvider.bufferToHex(payload.authInfoBytes),
101 | },
102 | },
103 | }),
104 | );
105 | });
106 |
107 | test('Cosmos Provider → signArbitrary -> payload is correct', async () => {
108 | const handler = jest.fn((_params: IHandlerParams) => Promise.resolve());
109 |
110 | new Web3Provider({
111 | strategy: AdapterStrategy.PROMISES,
112 | handler,
113 | }).registerProvider(cosmos);
114 |
115 | const payload = 'hello world';
116 | await cosmos.signArbitrary(chainId, account, payload);
117 |
118 | expect(handler).toHaveBeenCalledWith(
119 | expect.objectContaining({
120 | name: 'signMessage',
121 | network: CosmosProvider.NETWORK,
122 | params: {
123 | signerAddress: account,
124 | chainId,
125 | data: CosmosProvider.bufferToHex(payload),
126 | },
127 | }),
128 | );
129 | });
130 |
131 | test('Cosmos Provider → sendTx -> payload is correct', async () => {
132 | const handler = jest.fn((_params: IHandlerParams) => Promise.resolve('0x0'));
133 |
134 | new Web3Provider({
135 | strategy: AdapterStrategy.PROMISES,
136 | handler,
137 | }).registerProvider(cosmos);
138 |
139 | const payload = Buffer.from('mock tx');
140 | await cosmos.sendTx(chainId, payload, 'block' as BroadcastMode);
141 |
142 | expect(handler).toHaveBeenCalledWith(
143 | expect.objectContaining({
144 | name: 'sendTransaction',
145 | network: CosmosProvider.NETWORK,
146 | params: {
147 | chainId,
148 | raw: payload.toString('base64'),
149 | mode: 'block',
150 | },
151 | }),
152 | );
153 | });
154 |
155 | test('Cosmos Provider → getOfflineSignerAmino', async () => {
156 | const signer = cosmos.getOfflineSignerAmino(chainId);
157 |
158 | expect(signer).toHaveProperty('getAccounts');
159 | expect(signer).toHaveProperty('sign');
160 | expect(signer).toHaveProperty('signAmino');
161 | });
162 |
163 | test('Cosmos Provider → getOfflineSignerDirect', async () => {
164 | const signer = cosmos.getOfflineSignerDirect(chainId);
165 |
166 | expect(signer).toHaveProperty('getAccounts');
167 | expect(signer).toHaveProperty('signDirect');
168 | });
169 |
--------------------------------------------------------------------------------
/packages/cosmos/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/cosmos/types/CosmosProvider.ts:
--------------------------------------------------------------------------------
1 | import { StdSignDoc } from '@cosmjs/amino';
2 |
3 | export interface SignOptions {
4 | readonly preferNoSetFee?: boolean;
5 | readonly preferNoSetMemo?: boolean;
6 | readonly disableBalanceCheck?: boolean;
7 | }
8 |
9 | export declare enum BroadcastMode {
10 | /** Return after tx commit */
11 | Block = 'block',
12 | /** Return after CheckTx */
13 | Sync = 'sync',
14 | /** Return right away */
15 | Async = 'async',
16 | }
17 |
18 | export interface DirectSignDoc {
19 | /** SignDoc bodyBytes */
20 | bodyBytes: Uint8Array | Buffer;
21 | /** SignDoc authInfoBytes */
22 | authInfoBytes: Uint8Array | Buffer;
23 | /** SignDoc chainId */
24 | chainId: string | null;
25 | /** SignDoc accountNumber */
26 | accountNumber: bigint | null;
27 | }
28 |
29 | export interface ICosmosProvider {
30 | signAmino: (
31 | chainId: string,
32 | signer: string,
33 | signDoc: StdSignDoc,
34 | signOptions?: SignOptions,
35 | ) => Promise<{ signed: StdSignDoc; signature: string }>;
36 |
37 | signDirect: (
38 | chainId: string,
39 | signer: string,
40 | signDoc: DirectSignDoc,
41 | signOptions?: SignOptions,
42 | ) => Promise<{ signed: DirectSignDoc; signature: string }>;
43 |
44 | signArbitrary: (
45 | chainId: string,
46 | signer: string,
47 | data: string | Uint8Array,
48 | ) => Promise<{ signature: string }>;
49 |
50 | sendTx(
51 | chainId: string,
52 | tx: Uint8Array,
53 | mode: BroadcastMode,
54 | ): Promise;
55 | }
56 |
57 | export interface ICosmosProviderConfig {
58 | disableMobileAdapter?: boolean;
59 | isKeplr?: boolean;
60 | isTrust?: boolean;
61 | }
62 |
--------------------------------------------------------------------------------
/packages/ethereum/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 | // ___ ___ ___ __ ___
5 | // |__ | |__| |__ |__) |__ | | |\/|
6 | // |___ | | | |___ | \ |___ \__/ | |
7 | //
8 | ```
9 |
10 | ### Ethereum JavaScript Provider Implementation
11 |
12 | ### Config Object
13 |
14 | ```typescript
15 | const config: {
16 | rpc?: string;
17 | chainId?: string;
18 | overwriteMetamask?: boolean;
19 | supportedMethods?: string[];
20 | unsupportedMethods?: string[];
21 | disableMobileAdapter?: boolean;
22 | isTrust?: boolean;
23 | } = {};
24 | ```
25 |
26 | ### Usage
27 |
28 | ```typescript
29 | const ethereum = new EthereumProvider(config);
30 | ```
31 |
--------------------------------------------------------------------------------
/packages/ethereum/RPCServer.ts:
--------------------------------------------------------------------------------
1 | import { IRequestArguments } from './types';
2 | export interface RPC {
3 | call(payload: {
4 | jsonrpc: string;
5 | method: string;
6 | params: IRequestArguments['params'];
7 | }): Promise;
8 | }
9 |
10 | export class RPCServer implements RPC {
11 | #rpcUrl: string;
12 |
13 | constructor(rpcUrl: string) {
14 | this.#rpcUrl = rpcUrl;
15 | }
16 |
17 | async getBlockNumber() {
18 | const json = await this.call({
19 | jsonrpc: '2.0',
20 | method: 'eth_blockNumber',
21 | params: [],
22 | });
23 |
24 | return json.result;
25 | }
26 |
27 | async getBlockByNumber(number: number) {
28 | const json = await this.call({
29 | jsonrpc: '2.0',
30 | method: 'eth_getBlockByNumber',
31 | params: [number, false],
32 | });
33 |
34 | return json.result;
35 | }
36 |
37 | getFilterLogs(filter: string) {
38 | return this.call({
39 | jsonrpc: '2.0',
40 | method: 'eth_getLogs',
41 | params: [filter],
42 | });
43 | }
44 |
45 | async call(payload: {
46 | jsonrpc: string;
47 | method: string;
48 | params: IRequestArguments['params'];
49 | }) {
50 | const response = await fetch(this.#rpcUrl, {
51 | method: 'POST',
52 | headers: {
53 | Accept: 'application/json',
54 | 'Content-Type': 'application/json',
55 | },
56 | body: JSON.stringify({
57 | id: new Date().getTime() + Math.floor(Math.random() * 1000),
58 | ...payload,
59 | }),
60 | });
61 |
62 | const json = await response.json();
63 |
64 | if (!json.result && json.error) {
65 | throw new Error(json.error.message || 'rpc error');
66 | }
67 |
68 | return json.result;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/packages/ethereum/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/ethereum/index.ts:
--------------------------------------------------------------------------------
1 | export * from './EthereumProvider';
2 |
--------------------------------------------------------------------------------
/packages/ethereum/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-ethereum",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "@metamask/eth-sig-util": "^7.0.1",
23 | "@trustwallet/web3-provider-core": "workspace:*"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/ethereum/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/ethereum/tests/mocks/Wallet.ts:
--------------------------------------------------------------------------------
1 | export class EthereumWalletMock {
2 | requestAccounts() {}
3 | }
4 |
--------------------------------------------------------------------------------
/packages/ethereum/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/ethereum/types.ts:
--------------------------------------------------------------------------------
1 | import type IEthereumProvider from './types/EthereumProvider';
2 |
3 | export interface IRequestArguments {
4 | method: string;
5 | params?: unknown[] | object;
6 | }
7 |
8 | export type IProviderAccounts = string[];
9 |
10 | export interface IProviderMessage {
11 | type: string;
12 | data: unknown;
13 | }
14 |
15 | export interface IProviderInfo {
16 | chainId: string;
17 | }
18 |
19 | export interface IProviderRpcError extends Error {
20 | message: string;
21 | code: number;
22 | data?: unknown;
23 | }
24 |
25 | export type IProviderChainId = IProviderInfo['chainId'];
26 |
27 | export enum EmitterSupportedEvents {
28 | CONNECT = 'connect',
29 | DISCONNECT = 'disconnect',
30 | MESSAGE = 'message',
31 | CHAIN_CHANGED = 'chainChanged',
32 | ACCOUNTS_CHANGED = 'accountsChanged',
33 | }
34 |
35 | export declare namespace IProviderEvents {
36 | type IEvent =
37 | | 'connect'
38 | | 'disconnect'
39 | | 'message'
40 | | 'chainChanged'
41 | | 'accountsChanged';
42 |
43 | interface IEventArguments {
44 | connect: IProviderInfo;
45 | disconnect: IProviderRpcError;
46 | message: IProviderMessage;
47 | chainChanged: IProviderChainId;
48 | accountsChanged: IProviderAccounts;
49 | }
50 | }
51 |
52 | export interface IEthereumProviderListener {
53 | on: (
54 | event: E,
55 | listener: (args: IProviderEvents.IEventArguments[E]) => void,
56 | ) => IEthereumProvider;
57 |
58 | once: (
59 | event: E,
60 | listener: (args: IProviderEvents.IEventArguments[E]) => void,
61 | ) => IEthereumProvider;
62 |
63 | off: (
64 | event: E,
65 | listener: (args: IProviderEvents.IEventArguments[E]) => void,
66 | ) => IEthereumProvider;
67 |
68 | removeListener: (
69 | event: E,
70 | listener: (args: IProviderEvents.IEventArguments[E]) => void,
71 | ) => IEthereumProvider;
72 |
73 | emit: (
74 | event: E,
75 | payload: IProviderEvents.IEventArguments[E],
76 | ) => boolean;
77 | }
78 |
79 | export interface IWatchAsset {
80 | type: string;
81 | options: {
82 | type: string;
83 | address: string;
84 | symbol: string;
85 | decimals: number;
86 | };
87 | }
88 |
89 | export interface IPermissionRes {
90 | caveats: { type: string; value: string }[];
91 | }
92 |
--------------------------------------------------------------------------------
/packages/ethereum/types/EthereumProvider.ts:
--------------------------------------------------------------------------------
1 | import {
2 | EmitterSupportedEvents,
3 | IProviderAccounts,
4 | IProviderChainId,
5 | IProviderInfo,
6 | IProviderMessage,
7 | IProviderRpcError,
8 | IRequestArguments,
9 | } from '../types';
10 |
11 | export interface IEthereumProviderConfig {
12 | rpc?: string;
13 | address?: string;
14 | rpcUrl?: string;
15 | chainId?: string;
16 | overwriteMetamask?: boolean;
17 | supportedMethods?: string[];
18 | unsupportedMethods?: string[];
19 | disableMobileAdapter?: boolean;
20 | isTrust?: boolean;
21 | }
22 |
23 | /**
24 | * EIP-1193: Ethereum Provider JavaScript API
25 | *
26 | * https://eips.ethereum.org/EIPS/eip-1193
27 | */
28 | export default interface IEthereumProvider {
29 | request(args: IRequestArguments): Promise;
30 |
31 | setChainId(chainId: string): void;
32 |
33 | /**
34 | * @deprecated
35 | * @param args
36 | * @param callback
37 | */
38 | sendAsync(
39 | args: IRequestArguments,
40 | callback: (error: any | null, data: unknown | null) => void,
41 | ): void;
42 |
43 | on(
44 | event: EmitterSupportedEvents.CONNECT,
45 | listener: (info: IProviderInfo) => void,
46 | ): IEthereumProvider;
47 |
48 | on(
49 | event: EmitterSupportedEvents.DISCONNECT,
50 | listener: (error: IProviderRpcError) => void,
51 | ): IEthereumProvider;
52 |
53 | on(
54 | event: EmitterSupportedEvents.MESSAGE,
55 | listener: (message: IProviderMessage) => void,
56 | ): IEthereumProvider;
57 |
58 | on(
59 | event: EmitterSupportedEvents.CHAIN_CHANGED,
60 | listener: (chainId: IProviderChainId) => void,
61 | ): IEthereumProvider;
62 |
63 | on(
64 | event: EmitterSupportedEvents.ACCOUNTS_CHANGED,
65 | listener: (accounts: IProviderAccounts) => void,
66 | ): IEthereumProvider;
67 | }
68 |
69 | export enum RPCMethods {
70 | eth_requestAccounts = 'eth_requestAccounts',
71 | }
72 |
--------------------------------------------------------------------------------
/packages/ethereum/types/RPC.ts:
--------------------------------------------------------------------------------
1 | export declare type JsonRpcVersion = '2.0';
2 |
3 | export declare type JsonRpcId = number | string | void;
4 |
5 | export interface JsonRpcResponseBase {
6 | jsonrpc: JsonRpcVersion;
7 | id: JsonRpcId;
8 | }
9 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/.swiftpm/xcode/xcuserdata/fivezone.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TrustWeb3Provider.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 2
11 |
12 | ios-web3-provider-Package.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 | trust-web3-provider-Package.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 0
21 |
22 |
23 | SuppressBuildableAutocreation
24 |
25 | ios-web3-provider
26 |
27 | primary
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.5
2 |
3 | import PackageDescription
4 |
5 | let package = Package(
6 | name: "TrustWeb3Provider",
7 | products: [
8 | .library(
9 | name: "TrustWeb3Provider",
10 | targets: ["TrustWeb3Provider"]
11 | )
12 | ],
13 | dependencies: [],
14 | targets: [
15 | .target(
16 | name: "TrustWeb3Provider",
17 | dependencies: [],
18 | path: "swift",
19 | resources: [
20 | .process("trust-min.js", localization: .none)
21 | ]
22 | )
23 | ]
24 | )
25 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ### iOS Adapter
4 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/TrustWeb3Provider.podspec:
--------------------------------------------------------------------------------
1 | # Copyright © 2017-2022 Trust Wallet.
2 | #
3 | # This file is part of Trust. The full Trust copyright notice, including
4 | # terms governing use, modification, and redistribution, is contained in the
5 | # file LICENSE at the root of the source code distribution tree.
6 |
7 | Pod::Spec.new do |s|
8 | s.name = 'TrustWeb3Provider'
9 | s.version = '1.0.0'
10 | s.summary = 'Web3 javascript wrapper provider for iOS and Android platforms.'
11 |
12 | s.description = <<-DESC
13 | Web3 javascript wrapper provider for iOS and Android platforms.
14 | The magic behind the dApps browsers
15 | DESC
16 |
17 | s.homepage = 'https://github.com/TrustWallet/trust-web3-provider'
18 | s.license = { :type => 'MIT', :file => 'LICENSE' }
19 | s.author = { 'hewigovens' => 'hewigovens@gmail.com', 'Viktor Radchenko' => 'vikmeup' }
20 | s.source = { :git => 'https://github.com/TrustWallet/trust-web3-provider.git', :tag => s.version.to_s }
21 | s.social_media_url = 'https://twitter.com/TrustWallet'
22 |
23 | s.ios.deployment_target = '13.0'
24 | s.source_files = 'swift/*.swift'
25 | s.resource_bundles = {
26 | 'TrustWeb3Provider' => ['swift/trust-min.js']
27 | }
28 | end
29 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/global.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | interface Window {
3 | trustwallet: any;
4 | }
5 | }
6 |
7 | export {};
8 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/index.ts:
--------------------------------------------------------------------------------
1 | import { SolanaProvider } from '@trustwallet/web3-provider-solana';
2 | import { EthereumProvider } from '@trustwallet/web3-provider-ethereum';
3 | import { CosmosProvider } from '@trustwallet/web3-provider-cosmos';
4 | import { Web3Provider } from '@trustwallet/web3-provider-core';
5 | import {
6 | AdapterStrategyType,
7 | IHandler,
8 | } from '@trustwallet/web3-provider-core/adapter/Adapter';
9 | import { ISolanaProviderConfig } from '@trustwallet/web3-provider-solana/types/SolanaProvider';
10 | import { ICosmosProviderConfig } from '@trustwallet/web3-provider-cosmos/types/CosmosProvider';
11 | import { IEthereumProviderConfig } from '@trustwallet/web3-provider-ethereum/types/EthereumProvider';
12 | import { AptosProvider } from '@trustwallet/web3-provider-aptos';
13 | import { IAptosProviderConfig } from '@trustwallet/web3-provider-aptos/types/AptosProvider';
14 | import { ITonProviderConfig } from '@trustwallet/web3-provider-ton/types/TonProvider';
15 | import { ITonBridgeConfig } from '@trustwallet/web3-provider-ton/types/TonBridge';
16 | import { TonBridge, TonProvider } from '@trustwallet/web3-provider-ton';
17 |
18 | const core = (strategy: AdapterStrategyType, handler?: IHandler) =>
19 | new Web3Provider({ strategy, handler });
20 |
21 | const solana = (config: ISolanaProviderConfig) => new SolanaProvider(config);
22 |
23 | const cosmos = (config: ICosmosProviderConfig) => new CosmosProvider(config);
24 |
25 | const ethereum = (config: IEthereumProviderConfig) =>
26 | new EthereumProvider(config);
27 |
28 | const aptos = (config: IAptosProviderConfig) => new AptosProvider(config);
29 |
30 | const ton = (config: ITonProviderConfig) => new TonProvider(config);
31 |
32 | const tonBridge = (config: ITonBridgeConfig, provider: TonProvider) =>
33 | new TonBridge(config, provider);
34 |
35 | window.trustwallet = {
36 | core,
37 | solana,
38 | cosmos,
39 | ethereum,
40 | aptos,
41 | ton,
42 | tonBridge,
43 | };
44 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/Packages/WalletCore/.swiftpm/xcode/xcuserdata/fivezone.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SwiftProtobuf.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 4
11 |
12 | WalletCore.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 3
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/Packages/WalletCore/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "WalletCore",
6 | platforms: [.iOS(.v13)],
7 | products: [
8 | .library(name: "WalletCore", targets: ["WalletCore"]),
9 | .library(name: "SwiftProtobuf", targets: ["SwiftProtobuf"])
10 | ],
11 | dependencies: [],
12 | targets: [
13 | .binaryTarget(
14 | name: "WalletCore",
15 | url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.5/WalletCore.xcframework.zip",
16 | checksum: "10b50d569849349f82072325ca2d7cc549c2de4ece1c1ce2b2021d607ebcac17"
17 | ),
18 | .binaryTarget(
19 | name: "SwiftProtobuf",
20 | url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.5/SwiftProtobuf.xcframework.zip",
21 | checksum: "c84ebee2d15c0d310a60981eb69e0dfc41871fcc22ac8fbfa1677361506e73cf"
22 | )
23 | ]
24 | )
25 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '13.0'
2 |
3 | target 'PodsTest' do
4 | use_frameworks!
5 | pod 'TrustWeb3Provider', :path => '../../TrustWeb3Provider.podspec'
6 |
7 | target 'PodsTestTests' do
8 | inherit! :search_paths
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - TrustWeb3Provider (1.0.0)
3 |
4 | DEPENDENCIES:
5 | - TrustWeb3Provider (from `../../TrustWeb3Provider.podspec`)
6 |
7 | EXTERNAL SOURCES:
8 | TrustWeb3Provider:
9 | :path: "../../TrustWeb3Provider.podspec"
10 |
11 | SPEC CHECKSUMS:
12 | TrustWeb3Provider: 3542a569aa2d28031ac2edfe594e3563376df738
13 |
14 | PODFILE CHECKSUM: 6c87b8cefa328e150428f744e344b4b887098d7e
15 |
16 | COCOAPODS: 1.11.3
17 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest.xcodeproj/xcshareddata/xcschemes/PodsTest.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
53 |
55 |
61 |
62 |
63 |
64 |
70 |
72 |
78 |
79 |
80 |
81 |
83 |
84 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/ContentView.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2022 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import SwiftUI
8 | import TrustWeb3Provider
9 |
10 | struct ContentView: View {
11 | var body: some View {
12 | VStack {
13 | Image(systemName: "globe")
14 | .imageScale(.large)
15 | .foregroundColor(.accentColor)
16 | Text("Hello, world!")
17 | }
18 | .padding()
19 | }
20 | }
21 |
22 | struct ContentView_Previews: PreviewProvider {
23 | static var previews: some View {
24 | ContentView()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/PodsTestApp.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2022 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 | import SwiftUI
7 |
8 | @main
9 | struct PodsTestApp: App {
10 | var body: some Scene {
11 | WindowGroup {
12 | ContentView()
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTest/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/PodsTest/PodsTestTests/PodsTestTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2022 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import XCTest
8 | import TrustWeb3Provider
9 | @testable import PodsTest
10 |
11 | final class PodsTestTests: XCTestCase {
12 | func testLoadJs() throws {
13 | let ethereumConfig = TrustWeb3Provider.Config.EthereumConfig(
14 | address: "0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f",
15 | chainId: 1,
16 | rpcUrl: "https://cloudflare-eth.com"
17 | )
18 | let provider = TrustWeb3Provider(config: TrustWeb3Provider.Config(ethereum: ethereumConfig))
19 | let url = provider.providerJsUrl
20 | let data = try Data(contentsOf: url)
21 |
22 | XCTAssertTrue(data.count > 0)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/project.xcworkspace/xcuserdata/fivezone.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trustwallet/trust-web3-provider/e94d1c57bed40b90deedf04d3e56c8c817be9886/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/project.xcworkspace/xcuserdata/fivezone.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/xcshareddata/xcschemes/TrustWeb3Provider-Example.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider.xcodeproj/xcuserdata/fivezone.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TrustWeb3Provider-Example.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2020 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import UIKit
8 |
9 | @UIApplicationMain
10 | class AppDelegate: UIResponder, UIApplicationDelegate {
11 |
12 | var window: UIWindow?
13 |
14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
15 | return true
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/DAppMethod.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2020 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import Foundation
8 |
9 | enum DAppMethod: String, Decodable, CaseIterable {
10 | case signRawTransaction
11 | case signTransaction
12 | case signMessage
13 | case signTypedMessage
14 | case signPersonalMessage
15 | case sendTransaction
16 | case ecRecover
17 | case requestAccounts
18 | case watchAsset
19 | case addEthereumChain
20 | case switchEthereumChain // legacy compatible
21 | case switchChain
22 | }
23 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ios-marketing",
45 | "size" : "1024x1024",
46 | "scale" : "1x"
47 | }
48 | ],
49 | "info" : {
50 | "version" : 1,
51 | "author" : "xcode"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoadsInWebContent
28 |
29 |
30 | UILaunchStoryboardName
31 | LaunchScreen
32 | UIMainStoryboardFile
33 | Main
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/ios/TrustWeb3Provider/WKScriptMessage+JSON.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2020 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import Foundation
8 | import WebKit
9 |
10 | extension WKScriptMessage {
11 | var json: [String: Any] {
12 | if let string = body as? String,
13 | let data = string.data(using: .utf8),
14 | let object = try? JSONSerialization.jsonObject(with: data, options: []),
15 | let dict = object as? [String: Any] {
16 | return dict
17 | } else if let object = body as? [String: Any] {
18 | return object
19 | }
20 | return [:]
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/ios-web3-provider",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "@trustwallet/web3-provider-core": "workspace:*",
23 | "@trustwallet/web3-provider-ethereum": "workspace:*",
24 | "@trustwallet/web3-provider-solana": "workspace:*",
25 | "@trustwallet/web3-provider-cosmos": "workspace:*",
26 | "@trustwallet/web3-provider-aptos": "workspace:*",
27 | "@trustwallet/web3-provider-ton": "workspace:*",
28 | "rollup-plugin-polyfill-node": "0.13.0"
29 | },
30 | "devDependencies": {}
31 | }
32 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/rollup.config.js:
--------------------------------------------------------------------------------
1 | import esbuild from 'rollup-plugin-esbuild';
2 | import { nodeResolve } from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import { name, dependencies } from './package.json';
5 | import nodePolyfills from 'rollup-plugin-polyfill-node';
6 | import inject from '@rollup/plugin-inject';
7 | import json from '@rollup/plugin-json';
8 |
9 | const input = './index.ts';
10 | const plugins = [
11 | json(),
12 | nodeResolve({ preferBuiltins: false, browser: true }),
13 | commonjs(),
14 | inject({
15 | modules: {
16 | Buffer: ['buffer', 'Buffer'],
17 | },
18 | }),
19 | nodePolyfills(),
20 | esbuild({
21 | minify: true,
22 | tsconfig: './tsconfig.json',
23 | loaders: {
24 | '.json': 'json',
25 | },
26 | }),
27 | ];
28 |
29 | function createConfig(
30 | packageName,
31 | packageDependencies,
32 | umd = {},
33 | cjs = {},
34 | es = {},
35 | ) {
36 | return [
37 | {
38 | input,
39 | plugins,
40 | output: {
41 | file: './swift/trust-min.js',
42 | format: 'umd',
43 | exports: 'named',
44 | name: packageName,
45 | sourcemap: false,
46 | ...umd,
47 | },
48 | },
49 | ];
50 | }
51 |
52 | export default createConfig(name, Object.keys(dependencies));
53 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/swift/ProviderNetwork.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2022 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | public enum ProviderNetwork: String, Decodable {
8 | case ethereum
9 | case solana
10 | case cosmos
11 | case aptos
12 | case ton
13 | }
14 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/swift/WKWebView+Extension.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2017-2022 Trust Wallet.
2 | //
3 | // This file is part of Trust. The full Trust copyright notice, including
4 | // terms governing use, modification, and redistribution, is contained in the
5 | // file LICENSE at the root of the source code distribution tree.
6 |
7 | import WebKit
8 |
9 | public struct TypeWrapper {
10 | let value: T
11 |
12 | init(value: T) {
13 | self.value = value
14 | }
15 | }
16 |
17 | public extension WKWebView {
18 | var tw: TypeWrapper {
19 | return TypeWrapper(value: self)
20 | }
21 | }
22 |
23 | public extension TypeWrapper where T == WKWebView {
24 | func set(network: String, address: String) {
25 | let script = String(format: "trustwallet.\(network).setAddress(\"%@\");", address.lowercased())
26 | value.evaluateJavaScript(script)
27 | }
28 |
29 | func set(config: TrustWeb3Provider.Config) {
30 | let script = """
31 | var config = {
32 | ethereum: {
33 | address: "\(config.ethereum.address)",
34 | chainId: \(config.ethereum.chainId),
35 | rpcUrl: "\(config.ethereum.rpcUrl)"
36 | }
37 | };
38 | ethereum.setConfig(config);
39 | """
40 | value.evaluateJavaScript(script)
41 | }
42 |
43 | func emitChange(chainId: Int) {
44 | let string = "0x" + String(chainId, radix: 16)
45 | let script = String(format: "trustwallet.ethereum.emitChainChanged(\"%@\");", string)
46 | value.evaluateJavaScript(script)
47 | }
48 |
49 | func send(network: ProviderNetwork, error: String, to id: Int64) {
50 | let script = String(format: "trustwallet.\(network.rawValue).sendError(%ld, \"%@\")", id, error)
51 | value.evaluateJavaScript(script)
52 | }
53 |
54 | func send(network: ProviderNetwork, result: String, to id: Int64) {
55 | let script = String(format: "trustwallet.\(network.rawValue).sendResponse(%ld, \'%@\')", id, result)
56 | value.evaluateJavaScript(script)
57 | }
58 |
59 | func sendNull(network: ProviderNetwork, id: Int64) {
60 | let script = String(format: "trustwallet.\(network.rawValue).sendResponse(%ld, null)", id)
61 | value.evaluateJavaScript(script)
62 | }
63 |
64 | func send(network: ProviderNetwork, results: [String], to id: Int64) {
65 | let array = results.map { String(format: "\"%@\"", $0) }
66 | let script = String(format: "trustwallet.\(network.rawValue).sendResponse(%ld, [%@])", id, array.joined(separator: ","))
67 | value.evaluateJavaScript(script)
68 | }
69 |
70 | func removeScriptHandler() {
71 | value.configuration.userContentController.removeScriptMessageHandler(forName: TrustWeb3Provider.scriptHandlerName)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/swift/trust-min.js:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:e617351ded83fa70b56327bc39a882fa5cffa19a3e76e35bf3618cd74b16b757
3 | size 1123827
4 |
--------------------------------------------------------------------------------
/packages/ios-web3-provider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": [
4 | "package.json",
5 | "rollup.config.js",
6 | "tsconfig.json",
7 | "tests",
8 | "./dist"
9 | ],
10 | "compilerOptions": {
11 | "rootDir": "./",
12 | "outDir": "./dist/types",
13 | "emitDeclarationOnly": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/solana/MobileAdapter.ts:
--------------------------------------------------------------------------------
1 | import { IRequestArguments } from '@trustwallet/web3-provider-core';
2 | import { SolanaProvider } from './SolanaProvider';
3 | import { isVersionedTransaction } from './adapter/solana';
4 | import { PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js';
5 | import { ConnectOptions } from './types/SolanaProvider';
6 | import * as bs58 from 'bs58';
7 |
8 | /**
9 | * Adapting some requests to legacy mobile API
10 | *
11 | * This adapter provides the APIs with the method names and params the extension and mobile are
12 | * ready to handle
13 | */
14 | export class MobileAdapter {
15 | private provider!: SolanaProvider;
16 | private useLegacySign = false;
17 |
18 | constructor(provider: SolanaProvider, useLegacySign = false) {
19 | this.provider = provider;
20 | this.useLegacySign = useLegacySign;
21 | }
22 |
23 | async connect(
24 | options?: ConnectOptions | undefined,
25 | ): Promise<{ publicKey: PublicKey }> {
26 | const addresses = await this.provider.internalRequest({
27 | method: 'requestAccounts',
28 | params: { options },
29 | });
30 |
31 | this.provider.emit('connect');
32 | return { publicKey: new PublicKey(addresses[0]) };
33 | }
34 |
35 | async signTransaction(
36 | tx: T,
37 | ): Promise {
38 | if (this.useLegacySign) {
39 | return (await this.legacySign(tx)) as T;
40 | }
41 |
42 | const data = JSON.stringify(tx);
43 |
44 | let version: string | number = 'legacy';
45 | let rawMessage: string;
46 |
47 | if (isVersionedTransaction(tx)) {
48 | version = tx.version;
49 | rawMessage = Buffer.from(tx.message.serialize()).toString('base64');
50 | } else {
51 | rawMessage = Buffer.from(tx.serializeMessage()).toString('base64');
52 | }
53 |
54 | const raw = Buffer.from(
55 | tx.serialize({ requireAllSignatures: false, verifySignatures: false }),
56 | ).toString('base64');
57 |
58 | const signatureEncoded = await this.provider.internalRequest({
59 | method: 'signRawTransaction',
60 | params: {
61 | data,
62 | raw,
63 | rawMessage,
64 | version,
65 | },
66 | });
67 |
68 | return this.provider.mapSignedTransaction(tx, signatureEncoded);
69 | }
70 |
71 | async legacySign(tx: T) {
72 | const data = JSON.stringify(tx);
73 |
74 | const version =
75 | typeof (tx as VersionedTransaction).version !== 'number'
76 | ? 'legacy'
77 | : (tx as VersionedTransaction).version;
78 |
79 | const raw = bs58.encode(
80 | version === 'legacy'
81 | ? (tx as Transaction).serializeMessage()
82 | : version === 0
83 | ? (tx as VersionedTransaction).message.serialize()
84 | : tx.serialize(),
85 | );
86 |
87 | try {
88 | const signatureEncoded = await this.provider.internalRequest({
89 | method: 'signRawTransaction',
90 | params: { data, raw, version },
91 | });
92 |
93 | return this.provider.mapSignedTransaction(tx, signatureEncoded);
94 | } catch (error) {
95 | console.log(`<== Error: ${error}`);
96 | }
97 | }
98 |
99 | /**
100 | * Mobile adapter maps some solana methods to existing mobile method names
101 | * @param args
102 | * @param next
103 | * @returns
104 | */
105 | async request(
106 | args: IRequestArguments,
107 | next: () => Promise,
108 | ): Promise {
109 | switch (args.method) {
110 | case 'signTransaction': {
111 | return this.signTransaction(
112 | args.params as Transaction | VersionedTransaction,
113 | ) as unknown as T;
114 | }
115 |
116 | case 'connect': {
117 | return this.connect(
118 | (args?.params as { options: ConnectOptions })?.options,
119 | ) as unknown as T;
120 | }
121 | }
122 |
123 | return next();
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/packages/solana/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 |
5 | // __ __
6 | // /__` / \ | /\ |\ | /\
7 | // .__/ \__/ |___ /~~\ | \| /~~\
8 | //
9 |
10 | ```
11 |
12 | ### Solana JavaScript Provider Implementation that uses Wallet Standard
13 |
14 | ### Config Object
15 |
16 | ```typescript
17 | const config: {
18 | isTrust?: boolean;
19 | enableAdapter?: boolean;
20 | cluster?: string;
21 | disableMobileAdapter?: boolean;
22 | } = {};
23 | ```
24 |
25 | ### Usage
26 |
27 | ```typescript
28 | const solana = new SolanaProvider(config);
29 | ```
30 |
--------------------------------------------------------------------------------
/packages/solana/adapter/account.ts:
--------------------------------------------------------------------------------
1 | // This is copied with modification from @wallet-standard/wallet
2 |
3 | import {
4 | SolanaSignAndSendTransaction,
5 | SolanaSignMessage,
6 | SolanaSignTransaction,
7 | } from '@solana/wallet-standard-features';
8 | import type { WalletAccount } from '@wallet-standard/base';
9 | import { SOLANA_CHAINS } from './solana';
10 |
11 | const chains = SOLANA_CHAINS;
12 | const features = [
13 | SolanaSignAndSendTransaction,
14 | SolanaSignTransaction,
15 | SolanaSignMessage,
16 | ] as const;
17 |
18 | export class TrustWalletAccount implements WalletAccount {
19 | readonly #address: WalletAccount['address'];
20 | readonly #publicKey: WalletAccount['publicKey'];
21 | readonly #chains: WalletAccount['chains'];
22 | readonly #features: WalletAccount['features'];
23 | readonly #label: WalletAccount['label'];
24 | readonly #icon: WalletAccount['icon'];
25 |
26 | get address() {
27 | return this.#address;
28 | }
29 |
30 | get publicKey() {
31 | return this.#publicKey.slice();
32 | }
33 |
34 | get chains() {
35 | return this.#chains.slice();
36 | }
37 |
38 | get features() {
39 | return this.#features.slice();
40 | }
41 |
42 | get label() {
43 | return this.#label;
44 | }
45 |
46 | get icon() {
47 | return this.#icon;
48 | }
49 |
50 | constructor({
51 | address,
52 | publicKey,
53 | label,
54 | icon,
55 | }: Omit) {
56 | if (new.target === TrustWalletAccount) {
57 | Object.freeze(this);
58 | }
59 |
60 | this.#address = address;
61 | this.#publicKey = publicKey;
62 | this.#chains = chains;
63 | this.#features = features;
64 | this.#label = label;
65 | this.#icon = icon;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/solana/adapter/icon.ts:
--------------------------------------------------------------------------------
1 | import type { WalletIcon } from '@wallet-standard/base';
2 |
3 | export const icon: WalletIcon =
4 | 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTgiIGhlaWdodD0iNjUiIHZpZXdCb3g9IjAgMCA1OCA2NSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgOS4zODk0OUwyOC44OTA3IDBWNjUuMDA0MkM4LjI1NDUgNTYuMzM2OSAwIDM5LjcyNDggMCAzMC4zMzUzVjkuMzg5NDlaIiBmaWxsPSIjMDUwMEZGIi8+CjxwYXRoIGQ9Ik01Ny43ODIyIDkuMzg5NDlMMjguODkxNSAwVjY1LjAwNDJDNDkuNTI3NyA1Ni4zMzY5IDU3Ljc4MjIgMzkuNzI0OCA1Ny43ODIyIDMwLjMzNTNWOS4zODk0OVoiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8yMjAxXzY5NDIpIi8+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjIwMV82OTQyIiB4MT0iNTEuMzYxNSIgeTE9Ii00LjE1MjkzIiB4Mj0iMjkuNTM4NCIgeTI9IjY0LjUxNDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjAyMTEyIiBzdG9wLWNvbG9yPSIjMDAwMEZGIi8+CjxzdG9wIG9mZnNldD0iMC4wNzYyNDIzIiBzdG9wLWNvbG9yPSIjMDA5NEZGIi8+CjxzdG9wIG9mZnNldD0iMC4xNjMwODkiIHN0b3AtY29sb3I9IiM0OEZGOTEiLz4KPHN0b3Agb2Zmc2V0PSIwLjQyMDA0OSIgc3RvcC1jb2xvcj0iIzAwOTRGRiIvPgo8c3RvcCBvZmZzZXQ9IjAuNjgyODg2IiBzdG9wLWNvbG9yPSIjMDAzOEZGIi8+CjxzdG9wIG9mZnNldD0iMC45MDI0NjUiIHN0b3AtY29sb3I9IiMwNTAwRkYiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K' as const;
5 |
--------------------------------------------------------------------------------
/packages/solana/adapter/initialize.ts:
--------------------------------------------------------------------------------
1 | import ISolanaProvider from '../types/SolanaProvider';
2 | import { registerWallet } from './register';
3 |
4 | function initialize(trust: ISolanaProvider): void {
5 | registerWallet(trust.getInstanceWithAdapter());
6 | }
7 |
8 | export default initialize;
9 |
--------------------------------------------------------------------------------
/packages/solana/adapter/register.ts:
--------------------------------------------------------------------------------
1 | // This is copied from @wallet-standard/wallet
2 |
3 | import type {
4 | DEPRECATED_WalletsWindow,
5 | Wallet,
6 | WalletEventsWindow,
7 | WindowRegisterWalletEvent,
8 | WindowRegisterWalletEventCallback,
9 | } from '@wallet-standard/base';
10 |
11 | export function registerWallet(wallet: Wallet): void {
12 | const callback: WindowRegisterWalletEventCallback = ({ register }) =>
13 | register(wallet);
14 | try {
15 | (window as WalletEventsWindow).dispatchEvent(
16 | new RegisterWalletEvent(callback),
17 | );
18 | } catch (error) {
19 | console.error(
20 | 'wallet-standard:register-wallet event could not be dispatched\n',
21 | error,
22 | );
23 | }
24 | try {
25 | (window as WalletEventsWindow).addEventListener(
26 | 'wallet-standard:app-ready',
27 | ({ detail: api }) => callback(api),
28 | );
29 | } catch (error) {
30 | console.error(
31 | 'wallet-standard:app-ready event listener could not be added\n',
32 | error,
33 | );
34 | }
35 | }
36 |
37 | class RegisterWalletEvent extends Event implements WindowRegisterWalletEvent {
38 | readonly #detail: WindowRegisterWalletEventCallback;
39 |
40 | get detail() {
41 | return this.#detail;
42 | }
43 |
44 | get type() {
45 | return 'wallet-standard:register-wallet' as const;
46 | }
47 |
48 | constructor(callback: WindowRegisterWalletEventCallback) {
49 | super('wallet-standard:register-wallet', {
50 | bubbles: false,
51 | cancelable: false,
52 | composed: false,
53 | });
54 | this.#detail = callback;
55 | }
56 |
57 | /** @deprecated */
58 | preventDefault(): never {
59 | throw new Error('preventDefault cannot be called');
60 | }
61 |
62 | /** @deprecated */
63 | stopImmediatePropagation(): never {
64 | throw new Error('stopImmediatePropagation cannot be called');
65 | }
66 |
67 | /** @deprecated */
68 | stopPropagation(): never {
69 | throw new Error('stopPropagation cannot be called');
70 | }
71 | }
72 |
73 | /** @deprecated */
74 | export function DEPRECATED_registerWallet(wallet: Wallet): void {
75 | registerWallet(wallet);
76 | try {
77 | ((window as DEPRECATED_WalletsWindow).navigator.wallets ||= []).push(
78 | ({ register }) => register(wallet),
79 | );
80 | } catch (error) {
81 | console.error('window.navigator.wallets could not be pushed\n', error);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/packages/solana/adapter/solana.ts:
--------------------------------------------------------------------------------
1 | // This is copied from @solana/wallet-standard-chains
2 |
3 | import type { IdentifierString } from '@wallet-standard/base';
4 | import type { Transaction, VersionedTransaction } from '@solana/web3.js';
5 |
6 | /** Solana Mainnet (beta) cluster, e.g. https://api.mainnet-beta.solana.com */
7 | export const SOLANA_MAINNET_CHAIN = 'solana:mainnet';
8 |
9 | /** Solana Devnet cluster, e.g. https://api.devnet.solana.com */
10 | export const SOLANA_DEVNET_CHAIN = 'solana:devnet';
11 |
12 | /** Solana Testnet cluster, e.g. https://api.testnet.solana.com */
13 | export const SOLANA_TESTNET_CHAIN = 'solana:testnet';
14 |
15 | /** Solana Localnet cluster, e.g. http://localhost:8899 */
16 | export const SOLANA_LOCALNET_CHAIN = 'solana:localnet';
17 |
18 | /** Array of all Solana clusters */
19 | export const SOLANA_CHAINS = [SOLANA_MAINNET_CHAIN] as const;
20 |
21 | /** Type of all Solana clusters */
22 | export type SolanaChain = (typeof SOLANA_CHAINS)[number];
23 |
24 | /**
25 | * Check if a chain corresponds with one of the Solana clusters.
26 | */
27 | export function isSolanaChain(chain: IdentifierString): chain is SolanaChain {
28 | return SOLANA_CHAINS.includes(chain as SolanaChain);
29 | }
30 |
31 | export function isVersionedTransaction(
32 | transaction: Transaction | VersionedTransaction,
33 | ): transaction is VersionedTransaction {
34 | return 'version' in transaction;
35 | }
36 |
--------------------------------------------------------------------------------
/packages/solana/adapter/window.ts:
--------------------------------------------------------------------------------
1 | export interface TrustEvent {
2 | connect(...args: unknown[]): unknown;
3 | disconnect(...args: unknown[]): unknown;
4 | accountChanged(...args: unknown[]): unknown;
5 | }
6 |
7 | export interface TrustEventEmitter {
8 | on(
9 | event: E,
10 | listener: TrustEvent[E],
11 | context?: any,
12 | ): void;
13 | off(
14 | event: E,
15 | listener: TrustEvent[E],
16 | context?: any,
17 | ): void;
18 | }
19 |
--------------------------------------------------------------------------------
/packages/solana/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/solana/index.ts:
--------------------------------------------------------------------------------
1 | export * from './SolanaProvider';
2 |
--------------------------------------------------------------------------------
/packages/solana/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-solana",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "@solana/wallet-standard-features": "1.2.0",
23 | "@solana/web3.js": "1.91.8",
24 | "@wallet-standard/base": "1.1.0",
25 | "@wallet-standard/features": "1.0.3",
26 | "bs58": "4.0.1",
27 | "@trustwallet/web3-provider-core": "workspace:*",
28 | "rpc-websockets": "7.11.0"
29 | },
30 | "devDependencies": {
31 | "@types/bs58": "^4.0.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/solana/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/solana/tests/SolanaProvider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { SolanaProvider } from '../SolanaProvider';
4 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
5 | import { window } from './mocks/window';
6 | import { IHandlerParams } from '@trustwallet/web3-provider-core/adapter/CallbackAdapter';
7 | import {
8 | PublicKey,
9 | SystemProgram,
10 | Transaction,
11 | VersionedTransaction,
12 | } from '@solana/web3.js';
13 | import { StandardConnect } from '@wallet-standard/features';
14 | import {
15 | SolanaSignMessage,
16 | SolanaSignTransaction,
17 | } from '@solana/wallet-standard-features';
18 |
19 | // Mock window to allow solana adapter to work
20 | global.window = window;
21 |
22 | let Solana = new SolanaProvider();
23 | const account = '3z9vL1zjN6qyAFHhHQdWYRTFAcy69pJydkZmSFBKHg1R';
24 | const signature =
25 | '5LrcE2f6uvydKRquEJ8xp19heGxSvqsVbcqUeFoiWbXe8JNip7ftPQNTAVPyTK7ijVdpkzmKKaAQR7MWMmujAhXD';
26 |
27 | function transaction() {
28 | const accountPublicKey = new PublicKey(account);
29 | const transaction = new Transaction().add(
30 | SystemProgram.transfer({
31 | fromPubkey: accountPublicKey,
32 | toPubkey: accountPublicKey,
33 | lamports: 100,
34 | }),
35 | );
36 |
37 | transaction.feePayer = accountPublicKey;
38 | transaction.recentBlockhash = '6VdVbpsv7b5cSekEimjMTddydrikUsbeXQcizEk6LqSn';
39 |
40 | return transaction;
41 | }
42 |
43 | afterEach(() => {
44 | Solana = new SolanaProvider();
45 | });
46 |
47 | test('Solana Provider → connect -> payload is correct', async () => {
48 | const handler = jest.fn((_params: IHandlerParams) =>
49 | Promise.resolve([account]),
50 | );
51 |
52 | new Web3Provider({
53 | strategy: AdapterStrategy.PROMISES,
54 | handler: handler,
55 | }).registerProvider(Solana);
56 |
57 | await window.wallet.features[StandardConnect].connect();
58 |
59 | //await Solana.getInstanceWithAdapter().connect();
60 |
61 | expect(handler).toHaveBeenCalledWith(
62 | expect.objectContaining({
63 | name: 'requestAccounts',
64 | network: 'solana',
65 | }),
66 | );
67 | });
68 |
69 | test('Solana Provider → signMessage -> payload is correct', async () => {
70 | const handler = jest.fn((_params: IHandlerParams) =>
71 | Promise.resolve([account]),
72 | );
73 |
74 | new Web3Provider({
75 | strategy: AdapterStrategy.PROMISES,
76 | handler: handler,
77 | }).registerProvider(Solana);
78 |
79 | const accountConnect = await window.wallet.features[
80 | StandardConnect
81 | ].connect();
82 |
83 | await window.wallet.features[SolanaSignMessage].signMessage({
84 | account: accountConnect.accounts[0],
85 | message: Buffer.from('Random message'),
86 | });
87 |
88 | expect(handler).toHaveBeenCalledWith(
89 | expect.objectContaining({
90 | name: 'signMessage',
91 | network: 'solana',
92 | params: {
93 | data: `0x${Buffer.from('Random message').toString('hex')}`,
94 | },
95 | }),
96 | );
97 | });
98 |
99 | test('Solana Provider → signTransaction -> payload is correct for legacy', async () => {
100 | const handler = jest.fn((_params: IHandlerParams) => {
101 | if (_params.name === 'requestAccounts') {
102 | return Promise.resolve([account]);
103 | } else {
104 | return Promise.resolve(signature);
105 | }
106 | });
107 |
108 | const transactionPayload = transaction();
109 |
110 | new Web3Provider({
111 | strategy: AdapterStrategy.PROMISES,
112 | handler: handler,
113 | }).registerProvider(Solana);
114 |
115 | const accountConnect = await window.wallet.features[
116 | StandardConnect
117 | ].connect();
118 |
119 | await window.wallet.features[SolanaSignTransaction].signTransaction({
120 | account: accountConnect.accounts[0],
121 | transaction: transactionPayload.serialize({ verifySignatures: false }),
122 | });
123 |
124 | expect(handler).toHaveBeenNthCalledWith(
125 | 2,
126 | expect.objectContaining({
127 | name: 'signRawTransaction',
128 | network: 'solana',
129 | params: expect.objectContaining({
130 | raw: Buffer.from(
131 | transactionPayload.serialize({
132 | requireAllSignatures: false,
133 | verifySignatures: false,
134 | }),
135 | ).toString('base64'),
136 |
137 | rawMessage: Buffer.from(transactionPayload.serializeMessage()).toString(
138 | 'base64',
139 | ),
140 |
141 | version: 'legacy',
142 |
143 | data: JSON.stringify(
144 | VersionedTransaction.deserialize(
145 | transactionPayload.serialize({ verifySignatures: false }),
146 | ),
147 | ),
148 | }),
149 | }),
150 | );
151 | });
152 |
--------------------------------------------------------------------------------
/packages/solana/tests/mocks/window.ts:
--------------------------------------------------------------------------------
1 | import { TrustWallet } from '../../adapter/wallet';
2 | export let provider = null;
3 |
4 | export const window = {
5 | wallet: null,
6 |
7 | dispatchEvent: (event: Event) => {
8 | const $this = window;
9 |
10 | // Mock wallet standard registration system to window object
11 | if (event.type === 'wallet-standard:register-wallet') {
12 | (event as any).detail({
13 | register: (wallet: TrustWallet) => {
14 | $this.wallet = wallet;
15 | },
16 | });
17 | }
18 |
19 | return true;
20 | },
21 |
22 | addEventListener: () => {},
23 | } as unknown as Window & typeof globalThis & { wallet: TrustWallet };
24 |
--------------------------------------------------------------------------------
/packages/solana/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/solana/types/SolanaProvider.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SolanaSignInInput,
3 | SolanaSignInOutput,
4 | } from '@solana/wallet-standard-features';
5 | import {
6 | PublicKey,
7 | Transaction,
8 | VersionedTransaction,
9 | SendOptions,
10 | TransactionSignature,
11 | } from '@solana/web3.js';
12 | import { TrustEventEmitter } from '../adapter/window';
13 | import { TrustWallet } from '../adapter/wallet';
14 |
15 | export interface ISolanaProviderConfig {
16 | isTrust?: boolean;
17 | enableAdapter?: boolean;
18 | cluster?: string;
19 | disableMobileAdapter?: boolean;
20 | useLegacySign?: boolean;
21 | }
22 |
23 | export interface ConnectOptions {
24 | onlyIfTrusted?: boolean | undefined;
25 | }
26 |
27 | export default interface ISolanaProvider extends TrustEventEmitter {
28 | publicKey: PublicKey | null;
29 | connect(options?: {
30 | onlyIfTrusted?: boolean;
31 | }): Promise<{ publicKey: PublicKey }>;
32 | disconnect(): Promise;
33 | signAndSendTransaction(
34 | transaction: T,
35 | options?: SendOptions,
36 | ): Promise<{ signature: TransactionSignature }>;
37 | signTransaction(
38 | transaction: T,
39 | ): Promise;
40 | signAllTransactions(
41 | transactions: T[],
42 | ): Promise;
43 | signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }>;
44 | signIn(input?: SolanaSignInInput): Promise;
45 | getInstanceWithAdapter(): TrustWallet;
46 | }
47 |
--------------------------------------------------------------------------------
/packages/solana/util.ts:
--------------------------------------------------------------------------------
1 | // This is copied from @wallet-standard/wallet
2 |
3 | export function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
4 | return arraysEqual(a, b);
5 | }
6 |
7 | interface Indexed {
8 | length: number;
9 | [index: number]: T;
10 | }
11 |
12 | export function arraysEqual(a: Indexed, b: Indexed): boolean {
13 | if (a === b) return true;
14 |
15 | const length = a.length;
16 | if (length !== b.length) return false;
17 |
18 | for (let i = 0; i < length; i++) {
19 | if (a[i] !== b[i]) return false;
20 | }
21 |
22 | return true;
23 | }
24 |
--------------------------------------------------------------------------------
/packages/ton/README.md:
--------------------------------------------------------------------------------
1 | # Trust Web3 Provider
2 |
3 | ```
4 | ___ ___ ___
5 | /\ \ /\ \ /\__\
6 | \:\ \ /::\ \ /:| _|_
7 | /::\__\ /:/\:\__\ /::|/\__\
8 | /:/\/__/ \:\/:/ / \/|::/ /
9 | \/__/ \::/ / |:/ /
10 | \/__/ \/__/
11 | ```
12 |
13 | ### Ton JavaScript Provider Implementation
14 |
15 | ### Config Object
16 |
17 | ```typescript
18 | const config: {
19 | isTrust?: boolean;
20 | disableMobileAdapter?: boolean;
21 | version?: string;
22 | } = {};
23 | ```
24 |
25 | ### Usage
26 |
27 | ```typescript
28 | const ton = new TonProvider(config);
29 | ```
30 |
31 | ### Bridge usage
32 |
33 | ```typescript
34 | const bridgeConfig: {
35 | isWalletBrowser: boolean;
36 | walletInfo: WalletInfo;
37 | deviceInfo: DeviceInfo;
38 | } = {};
39 |
40 | const bridge = new TonBridge(ton, bridgeConfig);
41 | ```
42 |
--------------------------------------------------------------------------------
/packages/ton/TonProvider.ts:
--------------------------------------------------------------------------------
1 | import { BaseProvider } from '@trustwallet/web3-provider-core';
2 | import type ITonProvider from './types/TonProvider';
3 | import type { ITonProviderConfig } from './types/TonProvider';
4 | import { MobileAdapter } from './MobileAdapter';
5 |
6 | export class TonProvider extends BaseProvider implements ITonProvider {
7 | static NETWORK = 'ton';
8 | private mobileAdapter!: MobileAdapter;
9 |
10 | version = 'v4R2';
11 |
12 | constructor(config?: ITonProviderConfig) {
13 | super();
14 |
15 | if (config) {
16 | if (config.version) {
17 | this.version = config.version;
18 | }
19 | }
20 |
21 | if (!config?.disableMobileAdapter) {
22 | this.mobileAdapter = new MobileAdapter(this);
23 | }
24 | }
25 |
26 | disconnect() {
27 | return this.send('tonConnect_disconnect', {});
28 | }
29 |
30 | isConnected(): Promise {
31 | return Promise.resolve(true);
32 | }
33 |
34 | async send(method: string, params?: unknown[] | object): Promise {
35 | const next = () => {
36 | return this.internalRequest(method, params) as Promise;
37 | };
38 |
39 | if (this.mobileAdapter) {
40 | return await this.mobileAdapter.request(method, params);
41 | }
42 |
43 | return await next();
44 | }
45 |
46 | internalRequest(
47 | method: string,
48 | params: object | unknown[] | undefined,
49 | ): Promise {
50 | return super.request({
51 | method,
52 | params,
53 | });
54 | }
55 |
56 | getNetwork(): string {
57 | return TonProvider.NETWORK;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/packages/ton/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/ton/exceptions/TonConnectError.ts:
--------------------------------------------------------------------------------
1 | export class TonConnectError extends Error {
2 | code?: number;
3 |
4 | constructor(message: string, code?: number) {
5 | super(message);
6 | this.code = code;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/ton/index.ts:
--------------------------------------------------------------------------------
1 | export * from './TonProvider';
2 | export * from './TonBridge';
3 |
--------------------------------------------------------------------------------
/packages/ton/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-ton",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "peerDependencies": {},
22 | "dependencies": {
23 | "@ton/crypto": "3.3.0",
24 | "@ton/ton": "15.1.0",
25 | "@trustwallet/web3-provider-core": "workspace:*"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/ton/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/ton/tests/TonProvider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { TonProvider } from '../TonProvider';
4 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
5 | import { IHandlerParams } from '@trustwallet/web3-provider-core/adapter/CallbackAdapter';
6 |
7 | let Ton = new TonProvider();
8 | const account =
9 | '0:d4d9dc4b9024a4be47b8b35b02d24c679faf2d3d139c16462f9a6c34b8694cc0';
10 |
11 | afterEach(() => {
12 | Ton = new TonProvider();
13 | });
14 |
15 | test('Ton Connect → tonConnect_connect', async () => {
16 | new Web3Provider({
17 | strategy: AdapterStrategy.PROMISES,
18 | handler: () => Promise.resolve(JSON.stringify([{ address: account }])),
19 | }).registerProvider(Ton);
20 |
21 | const accounts = await Ton.send('tonConnect_connect');
22 | expect(accounts).toEqual([{ address: account }]);
23 | });
24 |
25 | test('Ton Connect → tonConnect_connect → mobile adapter', async () => {
26 | const handler = jest.fn((_params: IHandlerParams) =>
27 | Promise.resolve(JSON.stringify([{ address: account }])),
28 | );
29 |
30 | new Web3Provider({
31 | strategy: AdapterStrategy.PROMISES,
32 | handler,
33 | }).registerProvider(Ton);
34 |
35 | await Ton.send('tonConnect_connect');
36 |
37 | expect(handler).toHaveBeenCalledWith(
38 | expect.objectContaining({ name: 'requestAccounts' }),
39 | );
40 | });
41 |
42 | test('Ton Connect → ton_rawSign', async () => {
43 | const handler = jest.fn((_params: IHandlerParams) =>
44 | Promise.resolve(JSON.stringify([{ address: account }])),
45 | );
46 |
47 | new Web3Provider({
48 | strategy: AdapterStrategy.PROMISES,
49 | handler,
50 | }).registerProvider(Ton);
51 |
52 | await Ton.send('ton_rawSign', { message: '123' });
53 |
54 | expect(handler).toHaveBeenCalledWith(
55 | expect.objectContaining({
56 | name: 'signMessage',
57 | object: { message: '123' },
58 | }),
59 | );
60 | });
61 |
62 | test('Ton Connect → ton_sendTransaction', async () => {
63 | const handler = jest.fn((_params: IHandlerParams) =>
64 | Promise.resolve(JSON.stringify([{ name: 'ton_addr', address: account }])),
65 | );
66 |
67 | new Web3Provider({
68 | strategy: AdapterStrategy.PROMISES,
69 | handler,
70 | }).registerProvider(Ton);
71 |
72 | await Ton.send('tonConnect_connect');
73 | await Ton.send('ton_sendTransaction', [
74 | { network: '-239', messages: [], from: account, valid_until: 124 },
75 | ]);
76 |
77 | expect(handler).toHaveBeenCalledWith(
78 | expect.objectContaining({
79 | name: 'signTransaction',
80 | object: {
81 | network: '-239',
82 | messages: [],
83 | from: account,
84 | valid_until: 124,
85 | },
86 | }),
87 | );
88 | });
89 |
90 | test('Ton Connect → tonConnect_sendTransaction', async () => {
91 | const handler = jest.fn((_params: IHandlerParams) =>
92 | Promise.resolve(JSON.stringify([{ name: 'ton_addr', address: account }])),
93 | );
94 |
95 | new Web3Provider({
96 | strategy: AdapterStrategy.PROMISES,
97 | handler,
98 | }).registerProvider(Ton);
99 |
100 | await Ton.send('tonConnect_connect');
101 | await Ton.send('tonConnect_sendTransaction', [
102 | { network: '-239', messages: [], from: account, valid_until: 124 },
103 | ]);
104 |
105 | expect(handler).toHaveBeenCalledWith(
106 | expect.objectContaining({
107 | name: 'signTransaction',
108 | object: {
109 | network: '-239',
110 | messages: [],
111 | from: account,
112 | valid_until: 124,
113 | },
114 | }),
115 | );
116 | });
117 |
118 | test('Ton Connect → ton_requestAccounts', async () => {
119 | const handler = jest.fn((_params: IHandlerParams) =>
120 | Promise.resolve(JSON.stringify([{ nonBounceable: account }])),
121 | );
122 |
123 | new Web3Provider({
124 | strategy: AdapterStrategy.PROMISES,
125 | handler,
126 | }).registerProvider(Ton);
127 |
128 | const res = await Ton.send('ton_requestAccounts');
129 |
130 | expect(res).toEqual([account]);
131 |
132 | expect(handler).toHaveBeenCalledWith(
133 | expect.objectContaining({
134 | name: 'requestAccounts',
135 | }),
136 | );
137 | });
138 |
139 | test('Ton Connect → ton_requestWallets', async () => {
140 | const handler = jest.fn((_params: IHandlerParams) =>
141 | Promise.resolve(JSON.stringify([{ nonBounceable: account }])),
142 | );
143 |
144 | new Web3Provider({
145 | strategy: AdapterStrategy.PROMISES,
146 | handler,
147 | }).registerProvider(Ton);
148 |
149 | const res = await Ton.send('ton_requestWallets');
150 |
151 | expect(res).toEqual([{ address: account, version: 'v4R2' }]);
152 |
153 | expect(handler).toHaveBeenCalledWith(
154 | expect.objectContaining({
155 | name: 'requestAccounts',
156 | }),
157 | );
158 | });
159 |
--------------------------------------------------------------------------------
/packages/ton/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/ton/types/TonBridge.ts:
--------------------------------------------------------------------------------
1 | enum NETWORK {
2 | MAINNET = '-239',
3 | TESTNET = '-3',
4 | }
5 |
6 | type Feature =
7 | | { name: 'SendTransaction'; maxMessages: number } // `maxMessages` is maximum number of messages in one `SendTransaction` that the wallet supports
8 | | { name: 'SignData' };
9 |
10 | export type DeviceInfo = {
11 | platform: 'iphone' | 'ipad' | 'android' | 'windows' | 'mac' | 'linux';
12 | appName: string; // e.g. "Tonkeeper"
13 | appVersion: string; // e.g. "2.3.367"
14 | maxProtocolVersion: number;
15 | features: Feature[]; // list of supported features and methods in RPC
16 | // Currently there is only one feature -- 'SendTransaction';
17 | };
18 |
19 | export interface WalletInfo {
20 | name: string;
21 | image: string;
22 | tondns?: string;
23 | about_url: string;
24 | }
25 |
26 | export type ConnectRequest = {
27 | manifestUrl: string;
28 | items: ConnectItem[]; // data items to share with the app
29 | };
30 |
31 | type ConnectItem = TonAddressItem | TonProofItem;
32 |
33 | type TonAddressItem = {
34 | name: 'ton_addr';
35 | };
36 |
37 | type TonProofItem = {
38 | name: 'ton_proof';
39 | payload: string; // arbitrary payload, e.g. nonce + expiration timestamp.
40 | };
41 |
42 | // Untrusted data returned by the wallet.
43 | // If you need a guarantee that the user owns this address and public key, you need to additionally request a ton_proof.
44 | export type TonAddressItemReply = {
45 | name: 'ton_addr';
46 | address: string; // TON address raw (`0:`)
47 | network: NETWORK; // network global_id
48 | publicKey: string; // HEX string without 0x
49 | walletStateInit: string; // Base64 (not url safe) encoded state init cell for the wallet contract
50 | };
51 |
52 | type TonProofItemReply = TonProofItemReplySuccess | TonProofItemReplyError;
53 |
54 | export type TonProofItemReplySuccess = {
55 | name: 'ton_proof';
56 | proof: {
57 | timestamp: string; // 64-bit unix epoch time of the signing operation (seconds)
58 | domain: {
59 | lengthBytes: number; // AppDomain Length
60 | value: string; // app domain name (as url part, without encoding)
61 | };
62 | signature: string; // base64-encoded signature
63 | payload: string; // payload from the request
64 | };
65 | };
66 |
67 | type TonProofItemReplyError = {
68 | name: 'ton_addr';
69 | error: {
70 | code: number;
71 | message?: string;
72 | };
73 | };
74 |
75 | export type ConnectItemReply = TonAddressItemReply | TonProofItemReply;
76 |
77 | type ConnectEventSuccess = {
78 | event: 'connect';
79 | id: number;
80 | payload: {
81 | items: ConnectItemReply[];
82 | device: DeviceInfo;
83 | };
84 | };
85 |
86 | export type ConnectEventError = {
87 | event: 'connect_error';
88 | id: number;
89 | payload: {
90 | code: number;
91 | message: string;
92 | };
93 | };
94 |
95 | export type ConnectEvent = ConnectEventSuccess | ConnectEventError;
96 |
97 | export type WalletResponse = WalletResponseSuccess | WalletResponseError;
98 |
99 | interface WalletResponseSuccess {
100 | result: string;
101 | id: string;
102 | }
103 |
104 | export interface WalletResponseError {
105 | error: { code: number; message: string; data?: unknown };
106 | id: string;
107 | }
108 |
109 | export interface WalletEvent {
110 | event: WalletEventName;
111 | id?: string;
112 | payload: any;
113 | }
114 |
115 | type WalletEventName = 'connect' | 'connect_error' | 'disconnect';
116 |
117 | export interface AppRequest {
118 | method: string;
119 | params: string[];
120 | id: string;
121 | }
122 |
123 | export type TonConnectCallback = (event: WalletEvent) => void;
124 |
125 | export interface ITonBridgeConfig {
126 | isWalletBrowser: boolean;
127 | walletInfo: WalletInfo;
128 | deviceInfo: DeviceInfo;
129 | }
130 |
131 | export interface TonConnectBridge {
132 | deviceInfo: DeviceInfo;
133 | walletInfo?: WalletInfo;
134 | protocolVersion: number;
135 | isWalletBrowser: boolean;
136 | connect(
137 | protocolVersion: number,
138 | message: ConnectRequest,
139 | ): Promise;
140 | restoreConnection(): Promise;
141 | send(message: AppRequest): Promise;
142 | listen(callback: (event: WalletEvent) => void): () => void;
143 | }
144 |
--------------------------------------------------------------------------------
/packages/ton/types/TonProvider.ts:
--------------------------------------------------------------------------------
1 | export interface ITonProviderConfig {
2 | isTrust?: boolean;
3 | disableMobileAdapter?: boolean;
4 | version?: string;
5 | }
6 |
7 | export default interface ITonProvider {
8 | isConnected(): Promise;
9 | send(method: string, params?: unknown[] | object): Promise;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/tron/TronProvider.ts:
--------------------------------------------------------------------------------
1 | import { BaseProvider } from '@trustwallet/web3-provider-core';
2 | import type ITronProvider from './types/TronProvider';
3 | import type { ITronProviderConfig } from './types/TronProvider';
4 | import { TronWeb } from 'tronweb';
5 | import { SignedTransaction, Transaction } from 'tronweb/lib/esm/types';
6 |
7 | interface IRequestArguments {
8 | readonly method: string;
9 | readonly params?: unknown[] | object;
10 | }
11 |
12 | export class TronProvider extends BaseProvider implements ITronProvider {
13 | static NETWORK = 'tron';
14 |
15 | ready = false;
16 |
17 | tronWeb!: TronWeb;
18 |
19 | #config!: ITronProviderConfig;
20 |
21 | #node!: string;
22 |
23 | constructor(config?: ITronProviderConfig) {
24 | super();
25 |
26 | if (config) {
27 | this.#config = config;
28 | }
29 |
30 | this.on('accountsChanged', (accounts: string[]) => {
31 | if (accounts.length === 0) {
32 | window.postMessage(
33 | {
34 | message: {
35 | action: 'disconnect',
36 | data: {},
37 | },
38 | },
39 | '*',
40 | );
41 | } else {
42 | this.tronWeb.setAddress(accounts[0]);
43 | window.postMessage(
44 | {
45 | message: {
46 | action: 'accountsChanged',
47 | data: {
48 | address: accounts[0],
49 | },
50 | },
51 | },
52 | '*',
53 | );
54 | }
55 | });
56 | }
57 |
58 | getNetwork(): string {
59 | return TronProvider.NETWORK;
60 | }
61 |
62 | #internalRequest(args: IRequestArguments): Promise {
63 | return super.request(args);
64 | }
65 |
66 | request(args: IRequestArguments): Promise {
67 | const { method, params } = args;
68 |
69 | switch (method) {
70 | case 'tron_requestAccounts':
71 | return this.#requestAccounts();
72 | }
73 |
74 | return Promise.resolve(null);
75 | }
76 |
77 | setNode(url: string) {
78 | this.#node = url;
79 | this.tronWeb = new TronWeb({ fullHost: url });
80 | }
81 |
82 | async signMessage() {
83 | throw new Error('Not implemented signMessage');
84 | }
85 |
86 | async signMessageV2(
87 | data: string | Uint8Array | Array,
88 | ): Promise {
89 | return await this.#internalRequest({
90 | method: 'signMessage',
91 | params: {
92 | data,
93 | isEthSign: false,
94 | },
95 | });
96 | }
97 |
98 | async sign(
99 | transaction: T,
100 | ): Promise {
101 | if (typeof transaction === 'object') {
102 | const result = await this.#internalRequest({
103 | method: 'signTransaction',
104 | params: {
105 | transaction,
106 | raw: false,
107 | },
108 | });
109 |
110 | return result;
111 | } else if (typeof transaction === 'string') {
112 | return this.signMessageV2(transaction);
113 | } else {
114 | console.error('tx is not an object');
115 | throw new Error('Invalid TX format');
116 | }
117 | }
118 |
119 | async signTransactionOffline() {
120 | console.log('signTransactionOffline CALLED');
121 | }
122 |
123 | async #requestAccounts() {
124 | try {
125 | const accounts = await this.#internalRequest({
126 | method: 'requestAccounts',
127 | params: {},
128 | });
129 |
130 | if (accounts) {
131 | this.ready = true;
132 |
133 | this.tronWeb.trx.signMessageV2 = this.signMessageV2.bind(this) as any;
134 | this.tronWeb.trx.sign = this.sign.bind(this);
135 |
136 | this.tronWeb.setAddress(accounts[0]);
137 |
138 | window.postMessage(
139 | {
140 | message: {
141 | action: 'connect',
142 | data: {},
143 | },
144 | },
145 | '*',
146 | );
147 |
148 | return { code: 200 };
149 | }
150 | } catch (e) {
151 | console.error(e);
152 |
153 | return { code: 4001 };
154 | }
155 | }
156 |
157 | connect() {
158 | return this.#requestAccounts();
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/packages/tron/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/packages/tron/index.ts:
--------------------------------------------------------------------------------
1 | export * from './TronProvider';
2 |
--------------------------------------------------------------------------------
/packages/tron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-tron",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": {
22 | "tronweb": "6.0.1"
23 | },
24 | "devDependencies": {
25 | "@trustwallet/web3-provider-core": "workspace:*"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/tron/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/packages/tron/tests/TronProvider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { TronProvider } from '../TronProvider';
4 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
5 |
6 | let Tron = new TronProvider();
7 | const account = '0x0000000000000000000000000000000000000000';
8 |
9 | afterEach(() => {
10 | Tron = new TronProvider();
11 | Tron.setNode('https://foo.baz');
12 | });
13 |
14 | // Direct methods
15 | test('Tron Awesome test', async () => {
16 | new Web3Provider({
17 | strategy: AdapterStrategy.PROMISES,
18 | handler: () => Promise.resolve([account]),
19 | }).registerProvider(Tron);
20 |
21 | const accounts = await Tron.request({
22 | method: 'tron_requestAccounts',
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/packages/tron/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/tron/types/TronProvider.ts:
--------------------------------------------------------------------------------
1 | export interface ITronProviderConfig {
2 | isTrust?: boolean;
3 | nodeURL: string;
4 | }
5 |
6 | export default interface ITronProvider {}
7 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import esbuild from 'rollup-plugin-esbuild';
2 | import { nodeResolve } from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import json from '@rollup/plugin-json';
5 |
6 | const input = './index.ts';
7 | const plugins = [
8 | json(),
9 | nodeResolve({ preferBuiltins: false, browser: true }),
10 | commonjs(),
11 | esbuild({
12 | minify: true,
13 | tsconfig: './tsconfig.json',
14 | loaders: {
15 | '.json': 'json',
16 | },
17 | }),
18 | ];
19 |
20 | export default function createConfig(
21 | packageName,
22 | packageDependencies,
23 | umd = {},
24 | cjs = {},
25 | es = {},
26 | ) {
27 | return [
28 | {
29 | input,
30 | plugins,
31 | output: {
32 | file: './dist/index.umd.js',
33 | format: 'umd',
34 | exports: 'named',
35 | name: packageName,
36 | sourcemap: true,
37 | ...umd,
38 | },
39 | },
40 | {
41 | input,
42 | plugins,
43 | external: packageDependencies,
44 | output: [
45 | {
46 | file: './dist/index.cjs.js',
47 | format: 'cjs',
48 | exports: 'named',
49 | name: packageName,
50 | sourcemap: true,
51 | ...cjs,
52 | },
53 | {
54 | file: './dist/index.es.js',
55 | format: 'es',
56 | exports: 'named',
57 | name: packageName,
58 | sourcemap: true,
59 | ...es,
60 | },
61 | ],
62 | },
63 | ];
64 | }
65 |
--------------------------------------------------------------------------------
/scripts/build.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 | import { execSync } from 'child_process';
4 |
5 | const subpackagesDir = path.resolve(__dirname, '../packages');
6 |
7 | const directories = fs
8 | .readdirSync(subpackagesDir, { withFileTypes: true })
9 | .filter((dirent) => dirent.isDirectory())
10 | .map((dirent) => dirent.name);
11 |
12 | directories.forEach((directory) => {
13 | const dirPath = path.join(subpackagesDir, directory);
14 |
15 | console.log(`Building ${directory}`);
16 |
17 | try {
18 | execSync('bun run build:source', { stdio: 'inherit', cwd: dirPath });
19 | console.log(`Built ${directory}`);
20 | } catch (error) {
21 | console.error(`Failed to build ${directory}`);
22 | console.error(error);
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/scripts/generate.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import { mkdir, rm, readdir } from 'node:fs/promises';
3 | import { execSync } from 'child_process';
4 |
5 | const subpackagesDir = path.resolve(__dirname, '../packages');
6 | const chainName = (process.argv[2] || '').toLowerCase();
7 |
8 | if (!chainName) {
9 | throw new Error('Please enter a chain name');
10 | }
11 |
12 | async function generate() {
13 | console.log(`
14 | ___ ___ ___
15 | ___ / /\\ / /\\ / /\\ ___
16 | /__/\\ / /::\\ / /:/ / /::\\ /__/\\
17 | \\ \\:\\ / /:/\\:\\ / /:/ /__/:/\\:\\ \\ \\:\\
18 | \\__\\:\\ / /::\\ \\:\\ / /:/ _\\_ \\:\\ \\:\\ \\__\\:\\
19 | / /::\\ /__/:/\\:\\_\\:\\ /__/:/ /\\ /__/\\ \\:\\ \\:\\ / /::\\
20 | / /:/\\:\\ \\__\\/~|::\\/:/ \\ \\:\\ /:/ \\ \\:\\ \\:\\_\\/ / /:/\\:\\
21 | / /:/__\\/ | |:|::/ \\ \\:\\ /:/ \\ \\:\\_\\:\\ / /:/__\\/
22 | /__/:/ | |:|\\/ \\ \\:\\/:/ \\ \\:\\/:/ /__/:/
23 | \\__\\/ |__|:|~ \\ \\::/ \\ \\::/ \\__\\/
24 | \\__\\| \\__\\/ \\__\\/
25 |
26 | \n\n
27 | Generating a new chain provider....
28 | `);
29 |
30 | const base = path.join(subpackagesDir, chainName);
31 | // check if already exists
32 | try {
33 | await readdir(base);
34 | console.error(`Oops, directory ${base} already exists, aborting`);
35 | return;
36 | } catch {}
37 |
38 | try {
39 | // Generate new dir, fails if already exists
40 | const folders = ['tests', 'exceptions', 'types'];
41 | await mkdir(base);
42 | await Promise.all(
43 | folders.map((folder) =>
44 | mkdir(path.join(subpackagesDir, chainName, folder)),
45 | ),
46 | );
47 |
48 | // Execute template generation
49 | execSync(
50 | `simple-scaffold -q \\
51 | -t templates \\
52 | -o packages/${chainName} \\
53 | ${chainName.slice(0, 1).toUpperCase() + chainName.slice(1)} --log-level none`,
54 | {
55 | stdio: 'inherit',
56 | cwd: path.resolve(__dirname, '../'),
57 | },
58 | );
59 |
60 | console.log(`Boilerplate code generated for ${chainName} 🚀 Have Fun!`);
61 | } catch (e) {
62 | console.error(e);
63 | await rm(path.join(subpackagesDir, chainName), {
64 | recursive: true,
65 | force: true,
66 | });
67 | }
68 | }
69 |
70 | generate();
71 |
--------------------------------------------------------------------------------
/scripts/link.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 | import { execSync, exec } from 'child_process';
4 | import { allowedPackages } from './packages';
5 |
6 | const subpackagesDir = path.resolve(__dirname, '../packages');
7 |
8 | const directories = fs
9 | .readdirSync(subpackagesDir, { withFileTypes: true })
10 | .filter((dirent) => dirent.isDirectory())
11 | .map((dirent) => dirent.name)
12 | .filter((name) => allowedPackages.includes(name));
13 |
14 | let command = `npm link `;
15 |
16 | Promise.all(
17 | directories.map((directory) => {
18 | const dirPath = path.join(subpackagesDir, directory);
19 |
20 | console.log(`Building ${directory}`);
21 |
22 | try {
23 | execSync('bun build:clean', { stdio: 'inherit', cwd: dirPath });
24 | exec('tsc -w', { cwd: dirPath });
25 | exec('rollup --config ./rollup.config.js --watch', {
26 | cwd: dirPath,
27 | });
28 |
29 | setTimeout(
30 | () => execSync('npm link --silent', { stdio: 'inherit', cwd: dirPath }),
31 | 2000,
32 | );
33 | console.log(`Built ${directory}`);
34 | command += `@trustwallet/web3-provider-${directory} `;
35 | } catch (error) {
36 | console.error(`Failed to build ${directory}`);
37 | console.error(error);
38 | }
39 | }),
40 | ).then(() => console.warn(`\n\nUse the packages like this: \n\n${command}\n`));
41 |
--------------------------------------------------------------------------------
/scripts/packages.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * List of packages that are able to build and distribute
3 | */
4 | export const allowedPackages = [
5 | 'core',
6 | 'cosmos',
7 | 'ethereum',
8 | 'solana',
9 | 'aptos',
10 | 'ton',
11 | 'tron',
12 | ];
13 |
--------------------------------------------------------------------------------
/scripts/publish.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 | import { execSync } from 'child_process';
4 | import { allowedPackages } from './packages';
5 |
6 | /**
7 | * Executes publish to npm
8 | */
9 | const subpackagesDir = path.resolve(__dirname, '../packages');
10 |
11 | const directories = fs
12 | .readdirSync(subpackagesDir, { withFileTypes: true })
13 | .filter((dirent) => dirent.isDirectory())
14 | .map((dirent) => dirent.name)
15 | .filter((name) => allowedPackages.includes(name));
16 |
17 | directories.forEach((directory) => {
18 | const dirPath = path.join(subpackagesDir, directory);
19 |
20 | console.log(`Publishing ${directory}`);
21 |
22 | try {
23 | execSync(`npm publish --access public`, {
24 | stdio: 'inherit',
25 | cwd: dirPath,
26 | });
27 | console.log(`Published ${directory}`);
28 | } catch (error) {
29 | console.error(`Failed to build ${directory}`);
30 | console.error(error);
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/scripts/rename.ts:
--------------------------------------------------------------------------------
1 | import * as fs from 'fs';
2 | import * as path from 'path';
3 |
4 | /**
5 | * Renames dependencies version for packages
6 | * and version in package.json
7 | */
8 | const subpackagesDir = path.resolve(__dirname, '../packages');
9 | const version = process.argv[2];
10 |
11 | if (!version) {
12 | throw new Error('Invalid version');
13 | }
14 |
15 | const directories = fs
16 | .readdirSync(subpackagesDir, { withFileTypes: true })
17 | .filter((dirent) => dirent.isDirectory())
18 | .map((dirent) => dirent.name);
19 |
20 | directories.forEach((directory) => {
21 | const dirPath = path.join(subpackagesDir, directory);
22 | const packageJson = path.join(dirPath, 'package.json');
23 | const file = JSON.parse(fs.readFileSync(packageJson, 'utf-8'));
24 |
25 | file.version = version;
26 |
27 | if (directory !== 'core') {
28 | const update = (deps: any) => {
29 | if (deps) {
30 | Object.keys(deps).forEach((dep) => {
31 | if (deps[dep] === 'workspace:*') {
32 | deps[dep] = version;
33 | }
34 | });
35 | }
36 | };
37 |
38 | update(file.dependencies);
39 | }
40 |
41 | fs.writeFileSync(packageJson, JSON.stringify(file, null, 2));
42 | });
43 |
--------------------------------------------------------------------------------
/templates/exceptions/RPCError.ts:
--------------------------------------------------------------------------------
1 | export class RPCError extends Error {
2 | code: number;
3 |
4 | constructor(code: number, message: string) {
5 | super();
6 | this.code = code;
7 | this.message = message;
8 | }
9 |
10 | toString() {
11 | return `${this.message} (${this.code})`;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/templates/index.ts:
--------------------------------------------------------------------------------
1 | export * from './{{name}}Provider';
2 |
--------------------------------------------------------------------------------
/templates/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@trustwallet/web3-provider-{{camelCase name}}",
3 | "version": "4.0.0",
4 | "type": "module",
5 | "main": "dist/index.cjs.js",
6 | "module": "dist/index.es.js",
7 | "unpkg": "dist/index.umd.js",
8 | "types": "dist/types/index.d.ts",
9 | "author": "Trust ",
10 | "license": "MIT",
11 | "sideEffects": false,
12 | "files": [
13 | "dist"
14 | ],
15 | "scripts": {
16 | "build:clean": "rm -rf dist",
17 | "build:types": "tsc",
18 | "build:source": "bun build:clean; rollup --config ./rollup.config.js; bun run build:types",
19 | "dev": "bun build ./index.ts --outdir ./dist --watch"
20 | },
21 | "dependencies": { },
22 | "devDependencies": {
23 | "@trustwallet/web3-provider-core": "workspace:*"
24 | }
25 | }
--------------------------------------------------------------------------------
/templates/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { name, dependencies } from './package.json';
2 | import createConfig from '../../rollup.config';
3 |
4 | export default createConfig(name, Object.keys(dependencies));
5 |
--------------------------------------------------------------------------------
/templates/tests/{{ name }}Provider.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect, jest, afterEach } from 'bun:test';
2 | import { Web3Provider } from '@trustwallet/web3-provider-core';
3 | import { {{name}}Provider } from '../{{name}}Provider';
4 | import { AdapterStrategy } from '@trustwallet/web3-provider-core/adapter/Adapter';
5 |
6 | let {{name}} = new {{name}}Provider();
7 | const account = '0x0000000000000000000000000000000000000000';
8 |
9 | afterEach(() => {
10 | {{name}} = new {{name}}Provider();
11 | });
12 |
13 | // Direct methods
14 | test('{{name}} Awesome test', async () => {
15 | new Web3Provider({
16 | strategy: AdapterStrategy.PROMISES,
17 | handler: () => Promise.resolve([account]),
18 | }).registerProvider({{name}});
19 |
20 | const accounts = await {{name}}.request({ method: 'test_method' });
21 | expect(accounts).toEqual([account]);
22 | });
23 |
--------------------------------------------------------------------------------
/templates/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "exclude": ["package.json", "rollup.config.js", "tsconfig.json", "tests", "./dist"],
4 | "compilerOptions": {
5 | "rootDir": "./",
6 | "outDir": "./dist/types",
7 | "emitDeclarationOnly": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/templates/types/{{ name}}Provider.ts:
--------------------------------------------------------------------------------
1 | export interface I{{name}}ProviderConfig {
2 | isTrust?: boolean;
3 | }
4 |
5 | export default interface I{{name}}Provider {}
--------------------------------------------------------------------------------
/templates/{{ name }}Provider.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { BaseProvider } from '@trustwallet/web3-provider-core';
4 | import type I{{name}}Provider from './types/{{name}}Provider';
5 | import type { I{{name}}ProviderConfig } from './types/{{name}}Provider';
6 |
7 | export class {{name}}Provider
8 | extends BaseProvider
9 | implements I{{name}}Provider
10 | {
11 | static NETWORK = '{{name}}';
12 |
13 | constructor(config?: I{{ name }}ProviderConfig) {
14 | super();
15 | // Your constructor logic here for setting config
16 | }
17 |
18 | getNetwork(): string {
19 | return {{name}}Provider.NETWORK;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | // Enable latest features
4 | "lib": ["ES2020", "DOM"],
5 | "target": "ES2020",
6 | "module": "ES2020",
7 | "allowJs": false,
8 | "composite": true,
9 |
10 | // Bundler mode
11 | "moduleResolution": "node",
12 |
13 | // Best practices
14 | "strict": true,
15 | "skipLibCheck": true,
16 | "noFallthroughCasesInSwitch": true,
17 |
18 | // Some stricter flags (disabled by default)
19 | "noUnusedLocals": false,
20 | "noUnusedParameters": false,
21 | "noPropertyAccessFromIndexSignature": false
22 | }
23 | }
24 |
--------------------------------------------------------------------------------