{"id":13651560,"url":"https://github.com/soramitsu/fearless-utils-Android","last_synced_at":"2025-04-22T22:31:31.620Z","repository":{"id":38083781,"uuid":"278060389","full_name":"soramitsu/fearless-utils-Android","owner":"soramitsu","description":"fearless-utils-Android","archived":false,"fork":false,"pushed_at":"2024-02-14T07:35:06.000Z","size":1994,"stargazers_count":19,"open_issues_count":5,"forks_count":9,"subscribers_count":37,"default_branch":"develop","last_synced_at":"2024-05-21T11:39:37.142Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soramitsu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-07-08T10:32:34.000Z","updated_at":"2024-02-26T03:56:36.000Z","dependencies_parsed_at":"2024-01-03T05:14:26.902Z","dependency_job_id":"a41419f6-bef5-4639-8438-cbad959397dc","html_url":"https://github.com/soramitsu/fearless-utils-Android","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soramitsu%2Ffearless-utils-Android","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soramitsu%2Ffearless-utils-Android/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soramitsu%2Ffearless-utils-Android/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soramitsu%2Ffearless-utils-Android/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soramitsu","download_url":"https://codeload.github.com/soramitsu/fearless-utils-Android/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250333950,"owners_count":21413482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-02T02:00:50.517Z","updated_at":"2025-04-22T22:31:30.650Z","avatar_url":"https://github.com/soramitsu.png","language":"Kotlin","funding_links":[],"categories":["Mobile"],"sub_categories":[],"readme":"# Fearless Wallet native Android (Kotlin/Java) library\n\nFearless Wallet utils Android is a native Android library to help developers build native mobile apps for Substrate-based networks, e.g. Polkadot, Kusama, and Westend.\n\n# Table of contents\n\n* [Bip39](#bip39)\n* [JSON import/export](#json-import-export)\n    + [Import](#import)\n    + [Export](#export)\n* [Extensions](#extensions)\n    + [Hex](#hex)\n    + [Hashing](#hashing)\n* [Icon generation](#icon-generation)\n* [Junction Decoder](#junction-decoder)\n* [Runtime](#runtime)\n* [Scale](#scale)\n    + [Define a schema](#define-a-schema)\n    + [Create and encode structs](#create-and-encode-structs)\n    + [Decode and use structs](#decode-and-use-structs)\n    + [Standart Data Types](#standart-data-types)\n    + [Custom Data Types](#custom-data-types)\n    + [Default values](#default-values)\n    + [Nullable fields](#nullable-fields)\n* [SS58](#ss58)\n* [WSRPC](#wsrpc)\n\n## Bip39\n\nBip39 is the algorithm which provides an opportunity to use a list of words, called mnemonic, instead of a raw 32 byte seed. Library provides `Bip39` class to work with mnemonics:\n\n``` kotlin\nval bip39 = Bip39()\n\nval newMnemonic = bip39.generateMnemonic(length = MnemonicLength.TWELVE) // twelve words\nval entropy = bip39.generateEntropy(newMnemonic)\nval theSameMnemonic = bip39.generateMnemonic(entropy)\n\n```\nTo generate a seed, a `passphrase` is needed. Technically, it is a decoded derivation path (see [Junction Decoder](#junction-decoder))\n\n``` kotlin\nval seed = bip39.generateSeed(entropy, passphrase)\n```\n\n## JSON import/export\nLibrary provides support for decoding/encoding account information using JSON format, compatible with Polkadot.js\n\n### Import\nUsing `JsonSeedDecoder` you can perform decoding of the imported JSON:\n\n``` kotlin\nval decoder = JsonSeedDecoder(..)\n\ndecoder.extractImportMetaData(myJson) // does not perform full decoding (skips secret decrypting). Faster\ndecoder.decode(myJson, password) // performs full decoding. Slower\n```\n\n### Export\nUsing `JsonSeedEncoder` you can generate JSON out of account information:\n\n``` kotlin\nval encoder = JsonSeedEncoder(..)\n\nval json = encoder.generate(keypair, seed, password, name, encryptionType, genesis, addressByte)\n```\n\n## Extensions\nLibrary provides several extensions, that implement most common operations.\n### Hex\n\n``` kotlin\nfun ByteArray.toHexString(withPrefix: Boolean = false): String\nfun String.fromHex(): ByteArray\nfun String.requirePrefix(prefix: String): String\nfun String.requireHexPrefix(): String\n```\n\n### Hashing\n\n``` kotlin\nfun ByteArray.xxHash128(): ByteArray\nfun ByteArray.xxHash64(): ByteArray\n\nfun ByteArray.blake2b512(): ByteArray\nfun ByteArray.blake2b256(): ByteArray\nfun ByteArray.blake2b128(): ByteArray\n\nfun XXHash64.hash(bytes: ByteArray, seed: Long = 0): ByteArray\nfun BCMessageDigest.hashConcat(bytes: ByteArray): ByteArray\nfun XXHash64.hashConcat(bytes: ByteArray): ByteArray\n```\n\n## Icon generation\n\nThere's a support for default Polkadot.js icon generation using `IconGenerator`:\n\n``` kotlin\nval generator = IconGenerator()\nval drawable =  generator.getSvgImage(accountId, sizeInPixels)\n```\n\n## Junction Decoder\n\n`JunctionDecoder` provides support for derivation paths:\n\n``` kotlin\nval derivationPath: String = ...\nval decoder = JunctionDecoder()\n\nval passphrase = decoder.getPassword(derivationPath) // retrieve passphrase to use in entropy -\u003e seed generation\nval decodedPath = decoder.decodeDerivationPath(derivationPath)\n```\n\n## Runtime\n\nYou can create storage keys easily:\n\n``` kotlin\nval accountId: ByteArray = ..\n\nval bondedKey = Module.Staking.Bonded.storageKey(bytes)\nval accountInfoKey = Module.System.Account.storageKey(bytes)\n```\n\nIf you're missing some specific service/module, you can define it by your own:\n\n``` kotlin\n object Staking : Module(\"Staking\") {\n\n    object ActiveEra : Service\u003cUnit\u003e(Staking, \"ActiveEra\") {\n\n        override fun storageKey(storageArgs: Unit): String {\n            return StorageUtils.createStorageKey(\n                service = this,\n                identifier = null\n            )\n        }\n    }\n}\n```\n\n## Scale\n\nLibrary provides a convenient DSL to deal with scale encoding/decoding. Original codec reference: [Link](https://substrate.dev/docs/en/knowledgebase/advanced/codec).\n### Define a schema\n\n``` kotlin\nobject AccountData : Schema\u003cAccountData\u003e() {\n    val free by uint128()\n    val reserved by uint128()\n    val miscFrozen by uint128()\n    val feeFrozen by uint128()\n}\n```\n### Create and encode structs\n\n``` kotlin\nval struct = AccountData { data -\u003e\n    data[AccountData.free] = BigDecimal(\"1\")\n    data[AccountData.reserved] = BigInteger(\"0\")\n    data[AccountData.miscFrozen] = BigInteger(\"0\")\n    data[AccountData.feeFrozen] = BigInteger(\"0\")\n}\n\nval inHex = struct.toHexString() // encode\nval asBytes = struct.toByteArray() // or as byte array\n```\n###  Decode and use structs\n\n``` kotlin\nval inHex = ...\nval struct = AccountData.read(inHex)\nval free = struct[AccountData.free]\n```\n\n### Standart Data Types\nLibrary provides the support for the following data types:\n* Numbers: `uint8`, `uint16`, `uint32`, `uint64`, `uint128`, `uint(nBytes)`, `compactInt`, `byte`, `long`\n* Primitives: `bool`, `string`\n* Arrays:\n    * `sizedByteArray(n)` - only content is encoded/decoded), size is thus known in advance\n    * `byteArray` - size can vary, so the size is also encoded/decoded alongside with the content\n* Compound types:\n    * `vector\u003cD\u003e` - List of objects of the some data type\n    * `optional\u003cD\u003e` - Nullable container for other data type\n    * `pair\u003cD1, D2\u003e`\n    * `enum(D1, D2, D3...)` - like union in C, stores only one value at once, but this value can have different data type\n    * `enum\u003cE : Enum\u003e` - for classical Kotlin enum\n\n### Custom Data Types\nIf the decoding/encoding cannot be done using standart data types, you can create your own by extending `DataType\u003cT\u003e`:\n``` kotlin\nobject Delimiter : DataType\u003cByte\u003e() {\n    override fun conformsType(value: Any?): Boolean {\n        return value is Byte \u0026\u0026 value == 0\n    }\n\n    override fun read(reader: ScaleCodecReader): Byte {\n        val read = reader.readByte()\n\n        if (read != 0.toByte()) throw java.lang.IllegalArgumentException(\"Delimiter is not 0\")\n\n        return 0\n    }\n\n    override fun write(writer: ScaleCodecWriter, ignored: Byte) {\n        writer.writeByte(0)\n    }\n}\n```\nAnd use it in your schema using `custom()` keyword:\n``` kotlin\nobject CustomTypeTest : Schema\u003cCustomTypeTest\u003e() {\n    val delimiter by custom(Delimiter)\n}\n````\n\n### Default values\nYou can supply and default values for each field in the schema:\n``` kotlin\nobject DefaultValues : Schema\u003cDefaultValues\u003e() {\n    val bytes by sizedByteArray(length = 10, default = ByteArray(10))\n    val text by string(default = \"Default\")\n    val bigInteger by uint128(default = BigInteger.TEN)\n}\n```\n\n### Nullable fields\n\nBy default, all fields are non null. However, you can use `optional()` to change the default behavior:\n``` kotlin\nobject Person : Schema\u003cPerson\u003e() {\n    val friendName by string().optional() // friendName now is Field\u003cString?\u003e\n}\n```\n\n## SS58\n\nSS58 is an address format using in substate ecosystem. You can encode/decode address using `SS58Encoder`:\n\n``` kotlin\nval encoder = SS58Encoder()\nval address = encoder.encode(publicKey, addressByte)\nval accountId = encoder.decode(address)\n```\n\n## WSRPC\n\nLibrary provides an implementation of `SocketService`, which simplifies communication with the node: it provides a seamless error recovery, subscription mechanism.\n\n### Initialize socket\nTo create a socket service, you need to provide several parameters:\n\n``` kotlin\nval reconnector = Reconnector(..) // to configure reconnect strategy and scheduling executor\nval requestExecutor = RequestExecutor(..) // to configure sending executor\nval socketService = SocketService(gson, logger, websocketFactory, reconnector, requestExecutor)\n```\n\n### Use socket\n\n``` kotlin\nsocketService.start(url) // async connect\nsocketService.stop() // all subscriptions/pending requests are cancelled\nsocketService.switchUrl(newUrl) // stops current connection and start a new one\n\n// execute single request\nsocketService.executeRequest(runtimeRequest, deliveryType, object : SocketService.ResponseListener\u003cRpcResponse\u003e {\n            override fun onNext(response: RpcResponse) {\n                // success\n            }\n\n            override fun onError(throwable: Throwable) {\n                // unrecoverable error happened\n            }\n        })\n\n// subscribe to changes\nsocketService.subscribe(runtimeRequest, object : SocketService.ResponseListener\u003cSubscriptionChange\u003e {\n                override fun onNext(response: SubscriptionChange) {\n                   // change arrived\n                }\n\n                override fun onError(throwable: Throwable) {\n                   // unrecoverable error happened\n                }\n            })\n```\n\n### Reconnect strategy\n\nDuring setup of `Reconnector`, you can specify a `ReconnectStrategy`. There are several of them bundled with library:\n* `ConstantReconnectStrategy`\n* `LinearReconnectStrategy`\n* `ExponentialReconnectStrategy`. This is a default reconnect strategy.\n\nYou can create your own strategy by implementing `ReconnectStrategy` interface.\n\n### Delivery type\n\nWhile sending a request, you can specify a `DeliveryType`. Currently, there are 3 of them:\n* `AT_LEAST_ONCE` - attempts to send request until succeeded. This is a default delivery type.\n* `AT_MOST_ONCE` - send request once, reports error if attempt failed.\n* `ON_RECONNECT` - similar to `AT_LEAST_ONCE`, but remembers request and sends it on each reconnect. Currently used for subscription initiation.\n\n### Using with coroutines\n\nLibrary has a out-of-box support for coroutines:\n\n``` kotlin\nscope.launch {\n    val response = socketService.executeAsync(request, deliveryType) // suspend function\n}\n\nsocketService.subscriptionFlow(request).onEach { change -\u003e\n        // do stuff here\n}.launchIn(scope)\n```\n\n### Mappers\n\nThe mappers for most common types are provided:\n* `scale` - For scale-encoded values\n* `scaleCollection` - For list of scale-encoded values\n* `pojo` - for json values\n* `pojoList` - for list of json values\n\nAll mappers return a `nullable` result by default. You can add `nonNull()` modifier to change this behavior. In case of null result, the `RpcException` will be thrown.\n\n#### Usage\n\n``` kotlin\nscale().nonNull().map(response, gson)\n\n// or with coroutines adapter\nsocketService.executeAsync(request, deliveryType, mapper = scale().nonNull())\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoramitsu%2Ffearless-utils-Android","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoramitsu%2Ffearless-utils-Android","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoramitsu%2Ffearless-utils-Android/lists"}