From 4e30fab9d64d30898947e8f242744e885694a6ff Mon Sep 17 00:00:00 2001 From: "ian.he" Date: Thu, 11 Apr 2019 13:42:06 +1200 Subject: [PATCH] init --- .123trigger | 1 + .babelrc.js | 1 + .codeclimate.yml | 6 + .editorconfig | 10 + .gitignore | 18 + .npmignore | 1 + .nvmrc | 1 + .travis.yml | 20 + CHANGELOG.md | 138 + CONTRIBUTING.md | 45 + LICENSE | 201 ++ README.md | 40 + babel.config.js | 3 + jest.config.js | 30 + lerna.json | 13 + package.json | 35 + packages/api-derive/LICENSE | 201 ++ packages/api-derive/README.md | 7 + packages/api-derive/package.json | 39 + .../api-derive/src/accounts/idAndIndex.ts | 47 + packages/api-derive/src/accounts/idToIndex.ts | 21 + packages/api-derive/src/accounts/index.ts | 8 + packages/api-derive/src/accounts/indexToId.ts | 28 + packages/api-derive/src/accounts/indexes.ts | 42 + packages/api-derive/src/balances/fees.ts | 36 + packages/api-derive/src/balances/index.ts | 10 + .../src/balances/validatingBalance.ts | 41 + .../src/balances/validatingBalances.ts | 31 + .../api-derive/src/balances/votingBalance.ts | 44 + .../api-derive/src/balances/votingBalances.ts | 19 + .../balances/votingBalancesNominatorsFor.ts | 27 + packages/api-derive/src/chain/bestNumber.ts | 31 + .../src/chain/bestNumberFinalized.ts | 31 + .../api-derive/src/chain/bestNumberLag.ts | 36 + packages/api-derive/src/chain/getHeader.ts | 46 + packages/api-derive/src/chain/index.ts | 9 + .../api-derive/src/chain/subscribeNewHead.ts | 48 + packages/api-derive/src/democracy/index.ts | 8 + .../src/democracy/referendumInfo.ts | 66 + .../src/democracy/referendumInfos.ts | 26 + .../src/democracy/referendumVotesFor.ts | 36 + .../api-derive/src/democracy/referendums.ts | 31 + packages/api-derive/src/democracy/votes.ts | 24 + packages/api-derive/src/index.spec.ts | 88 + packages/api-derive/src/index.ts | 66 + packages/api-derive/src/session/eraLength.ts | 25 + .../api-derive/src/session/eraProgress.ts | 35 + packages/api-derive/src/session/index.ts | 7 + .../api-derive/src/session/sessionProgress.ts | 30 + .../api-derive/src/staking/controllers.ts | 35 + packages/api-derive/src/staking/index.ts | 6 + .../src/staking/intentionsBalances.ts | 25 + packages/api-derive/src/types.ts | 34 + packages/api-derive/src/util/drr.spec.ts | 32 + packages/api-derive/src/util/drr.ts | 18 + packages/api-derive/test/e2e/promise.spec.ts | 32 + packages/api-derive/test/e2e/rx.spec.ts | 76 + packages/api/LICENSE | 201 ++ packages/api/README.md | 98 + packages/api/package.json | 41 + packages/api/src/Base.ts | 667 ++++ packages/api/src/SubmittableExtrinsic.ts | 222 ++ packages/api/src/checkTypes.manual.ts | 46 + packages/api/src/index.ts | 12 + packages/api/src/promise/Api.ts | 241 ++ packages/api/src/promise/Combinator.ts | 86 + packages/api/src/promise/Combinators.spec.ts | 95 + packages/api/src/promise/index.ts | 7 + packages/api/src/promise/types.ts | 21 + packages/api/src/rx/Api.ts | 205 ++ packages/api/src/rx/index.ts | 7 + packages/api/src/rx/types.ts | 15 + packages/api/src/types.ts | 172 + packages/api/src/util/filterEvents.ts | 27 + packages/api/src/util/logging.ts | 9 + packages/api/test/data/erc20.json | 100 + packages/api/test/data/erc20.wasm | Bin 0 -> 31213 bytes packages/api/test/e2e/promise-alex.spec.js | 55 + .../api/test/e2e/promise-contract.spec.js | 92 + packages/api/test/e2e/promise-queries.spec.js | 87 + packages/api/test/e2e/promise-tx.spec.js | 214 ++ packages/api/test/e2e/rx-queries.spec.js | 53 + packages/api/test/e2e/rx-tx.spec.js | 61 + packages/api/test/util/SingleAccountSigner.ts | 28 + packages/keyring/LICENSE | 201 ++ packages/keyring/README.md | 21 + packages/keyring/package.json | 39 + packages/keyring/src/address/decode.spec.ts | 90 + packages/keyring/src/address/decode.ts | 46 + packages/keyring/src/address/defaults.ts | 15 + packages/keyring/src/address/encode.spec.ts | 65 + packages/keyring/src/address/encode.ts | 30 + packages/keyring/src/address/index.ts | 13 + .../keyring/src/address/setPrefix.spec.ts | 20 + packages/keyring/src/address/setPrefix.ts | 11 + packages/keyring/src/address/sshash.ts | 12 + packages/keyring/src/address/types.ts | 5 + packages/keyring/src/defaults.ts | 14 + packages/keyring/src/index.spec.ts | 149 + packages/keyring/src/index.ts | 242 ++ packages/keyring/src/pair/decode.spec.ts | 25 + packages/keyring/src/pair/decode.ts | 55 + packages/keyring/src/pair/defaults.ts | 19 + packages/keyring/src/pair/encode.spec.ts | 25 + packages/keyring/src/pair/encode.ts | 29 + packages/keyring/src/pair/index.spec.ts | 55 + packages/keyring/src/pair/index.ts | 104 + packages/keyring/src/pair/nobody.ts | 53 + packages/keyring/src/pair/toJson.spec.ts | 41 + packages/keyring/src/pair/toJson.ts | 29 + packages/keyring/src/pair/types.ts | 9 + packages/keyring/src/pairs.ts | 53 + packages/keyring/src/suri.spec.ts | 77 + packages/keyring/src/testing.ts | 74 + packages/keyring/src/testingPairs.spec.ts | 31 + packages/keyring/src/testingPairs.ts | 25 + packages/keyring/src/types.ts | 76 + packages/rpc-core/LICENSE | 201 ++ packages/rpc-core/README.md | 71 + packages/rpc-core/package.json | 38 + packages/rpc-core/src/formatting.spec.ts | 80 + packages/rpc-core/src/index.spec.ts | 39 + packages/rpc-core/src/index.ts | 250 ++ packages/rpc-core/src/methodSend.spec.ts | 66 + packages/rpc-core/src/types.ts | 21 + packages/rpc-core/test/e2e/basics.test.js | 42 + packages/rpc-core/test/e2e/chain.test.js | 46 + .../rpc-core/test/e2e/krummelanke.test.js | 63 + packages/rpc-core/test/e2e/state.test.js | 81 + packages/rpc-core/test/e2e/subscribe.test.js | 33 + packages/rpc-provider/LICENSE | 201 ++ packages/rpc-provider/README.md | 45 + packages/rpc-provider/package.json | 46 + .../src/coder/decodeResponse.spec.ts | 50 + .../rpc-provider/src/coder/encodeJson.spec.ts | 19 + .../src/coder/encodeObject.spec.ts | 29 + packages/rpc-provider/src/coder/index.ts | 64 + packages/rpc-provider/src/defaults.ts | 11 + packages/rpc-provider/src/http/index.spec.js | 43 + packages/rpc-provider/src/http/index.ts | 128 + .../rpc-provider/src/http/polyfill.spec.js | 26 + packages/rpc-provider/src/http/polyfill.ts | 8 + packages/rpc-provider/src/http/send.spec.js | 56 + packages/rpc-provider/src/http/types.ts | 13 + packages/rpc-provider/src/index.ts | 6 + packages/rpc-provider/src/mock/index.ts | 206 ++ packages/rpc-provider/src/mock/on.spec.ts | 31 + packages/rpc-provider/src/mock/send.spec.ts | 29 + .../rpc-provider/src/mock/subscribe.spec.ts | 63 + packages/rpc-provider/src/mock/types.ts | 22 + .../rpc-provider/src/mock/unsubscribe.spec.ts | 40 + packages/rpc-provider/src/types.ts | 54 + packages/rpc-provider/src/ws/Provider.ts | 412 +++ packages/rpc-provider/src/ws/connect.spec.js | 33 + packages/rpc-provider/src/ws/index.spec.js | 32 + packages/rpc-provider/src/ws/index.ts | 7 + .../src/ws/onMessageResult.spec.js | 24 + .../src/ws/onMessageSubscribe.spec.js | 47 + packages/rpc-provider/src/ws/onOpen.spec.js | 43 + packages/rpc-provider/src/ws/polyfill.spec.js | 26 + packages/rpc-provider/src/ws/polyfill.ts | 9 + packages/rpc-provider/src/ws/send.spec.js | 112 + packages/rpc-provider/src/ws/state.spec.js | 19 + .../rpc-provider/src/ws/subscribe.spec.js | 53 + .../rpc-provider/src/ws/unsubscribe.spec.js | 85 + packages/rpc-provider/test/mockHttp.ts | 27 + packages/rpc-provider/test/mockWs.ts | 83 + packages/rpc-rx/LICENSE | 201 ++ packages/rpc-rx/README.md | 7 + packages/rpc-rx/package.json | 40 + packages/rpc-rx/src/cached.spec.ts | 88 + packages/rpc-rx/src/index.spec.ts | 39 + packages/rpc-rx/src/index.ts | 184 + packages/rpc-rx/src/replay.spec.ts | 84 + packages/rpc-rx/src/types.ts | 24 + packages/rpc-rx/test/e2e/subscribe.test.js | 48 + packages/type-extrinsics/LICENSE | 201 ++ packages/type-extrinsics/README.md | 7 + packages/type-extrinsics/package.json | 17 + .../type-extrinsics/src/fromMetadata.spec.ts | 39 + packages/type-extrinsics/src/fromMetadata.ts | 58 + packages/type-extrinsics/src/index.spec.ts | 41 + packages/type-extrinsics/src/index.ts | 9 + packages/type-extrinsics/src/static.ts | 14 + .../src/utils/createUnchecked.ts | 44 + .../type-extrinsics/test/e2e/transfer.spec.js | 58 + packages/type-jsonrpc/LICENSE | 201 ++ packages/type-jsonrpc/README.md | 24 + packages/type-jsonrpc/package.json | 34 + packages/type-jsonrpc/src/author.ts | 54 + packages/type-jsonrpc/src/chain.ts | 106 + packages/type-jsonrpc/src/create/method.ts | 22 + packages/type-jsonrpc/src/create/param.ts | 18 + packages/type-jsonrpc/src/index.spec.js | 12 + packages/type-jsonrpc/src/index.ts | 19 + packages/type-jsonrpc/src/state.ts | 111 + packages/type-jsonrpc/src/system.ts | 73 + packages/type-jsonrpc/src/types.ts | 48 + packages/type-storage/LICENSE | 201 ++ packages/type-storage/README.md | 7 + packages/type-storage/package.json | 38 + .../type-storage/src/fromMetadata.spec.ts | 33 + packages/type-storage/src/fromMetadata.ts | 37 + packages/type-storage/src/index.ts | 7 + packages/type-storage/src/static.ts | 12 + packages/type-storage/src/storage.spec.ts | 17 + packages/type-storage/src/storage.ts | 10 + packages/type-storage/src/substrate.spec.ts | 33 + packages/type-storage/src/substrate.ts | 62 + packages/type-storage/src/types.ts | 18 + .../src/utils/createFunction.spec.ts | 56 + .../type-storage/src/utils/createFunction.ts | 75 + packages/types/LICENSE | 201 ++ packages/types/README.md | 23 + packages/types/package.json | 36 + packages/types/src/ContractAbi.ts | 148 + .../types/src/Metadata/MagicNumber.spec.ts | 15 + packages/types/src/Metadata/MagicNumber.ts | 18 + packages/types/src/Metadata/Metadata.ts | 44 + .../types/src/Metadata/MetadataVersioned.ts | 166 + packages/types/src/Metadata/README.md | 12 + packages/types/src/Metadata/index.ts | 7 + packages/types/src/Metadata/static.ts | 7 + packages/types/src/Metadata/types.ts | 7 + .../types/src/Metadata/util/flattenUniq.ts | 18 + packages/types/src/Metadata/util/index.ts | 6 + .../types/src/Metadata/util/validateTypes.ts | 50 + packages/types/src/Metadata/v0/Calls.ts | 62 + packages/types/src/Metadata/v0/Events.ts | 86 + .../types/src/Metadata/v0/Metadata.spec.ts | 42 + packages/types/src/Metadata/v0/Metadata.ts | 116 + packages/types/src/Metadata/v0/Modules.ts | 327 ++ packages/types/src/Metadata/v0/index.ts | 7 + .../src/Metadata/v0/latest.polkadot.json | 2429 +++++++++++++ .../src/Metadata/v0/latest.substrate.json | 2898 +++++++++++++++ packages/types/src/Metadata/v0/static.ts | 8 + packages/types/src/Metadata/v1/Calls.ts | 77 + packages/types/src/Metadata/v1/Events.ts | 44 + .../types/src/Metadata/v1/Metadata.spec.ts | 46 + packages/types/src/Metadata/v1/Metadata.ts | 130 + packages/types/src/Metadata/v1/Storage.ts | 165 + packages/types/src/Metadata/v1/index.ts | 11 + .../src/Metadata/v1/latest.substrate.v0.json | 2958 +++++++++++++++ .../src/Metadata/v1/latest.substrate.v1.json | 3009 ++++++++++++++++ packages/types/src/Metadata/v1/static.ts | 7 + packages/types/src/Metadata/v1/toV0.ts | 102 + .../types/src/Metadata/v2/Metadata.spec.ts | 52 + packages/types/src/Metadata/v2/Metadata.ts | 130 + packages/types/src/Metadata/v2/Storage.ts | 142 + packages/types/src/Metadata/v2/index.ts | 11 + .../src/Metadata/v2/latest.substrate.v2.json | 3093 ++++++++++++++++ packages/types/src/Metadata/v2/static.ts | 7 + packages/types/src/Metadata/v2/toV1.ts | 10 + .../types/src/Metadata/v3/Metadata.spec.ts | 22 + packages/types/src/Metadata/v3/Metadata.ts | 130 + packages/types/src/Metadata/v3/Storage.ts | 160 + packages/types/src/Metadata/v3/index.ts | 11 + .../src/Metadata/v3/latest.substrate.v3.json | 3204 +++++++++++++++++ packages/types/src/Metadata/v3/static.ts | 7 + packages/types/src/Metadata/v3/toV2.ts | 10 + packages/types/src/classes.ts | 7 + packages/types/src/codec/AbstractInt.ts | 131 + packages/types/src/codec/Base.ts | 15 + packages/types/src/codec/Compact.spec.ts | 161 + packages/types/src/codec/Compact.ts | 150 + packages/types/src/codec/Enum.spec.ts | 83 + packages/types/src/codec/Enum.ts | 130 + packages/types/src/codec/EnumType.spec.ts | 133 + packages/types/src/codec/EnumType.ts | 229 ++ packages/types/src/codec/Int.spec.ts | 51 + packages/types/src/codec/Int.ts | 55 + packages/types/src/codec/Linkage.ts | 42 + packages/types/src/codec/Option.spec.ts | 88 + packages/types/src/codec/Option.ts | 160 + packages/types/src/codec/Set.spec.ts | 73 + packages/types/src/codec/Set.ts | 142 + packages/types/src/codec/Struct.spec.ts | 186 + packages/types/src/codec/Struct.ts | 241 ++ packages/types/src/codec/Tuple.spec.ts | 97 + packages/types/src/codec/Tuple.ts | 152 + packages/types/src/codec/TypeRegistry.spec.ts | 66 + packages/types/src/codec/U8a.spec.ts | 72 + packages/types/src/codec/U8a.ts | 105 + packages/types/src/codec/U8aFixed.spec.ts | 33 + packages/types/src/codec/U8aFixed.ts | 55 + packages/types/src/codec/UInt.spec.ts | 70 + packages/types/src/codec/UInt.ts | 55 + packages/types/src/codec/Vector.spec.ts | 140 + packages/types/src/codec/Vector.ts | 185 + packages/types/src/codec/createType.spec.ts | 227 ++ packages/types/src/codec/createType.ts | 218 ++ packages/types/src/codec/index.ts | 26 + packages/types/src/codec/typeRegistry.ts | 73 + .../src/codec/utils/compareArray.spec.ts | 48 + .../types/src/codec/utils/compareArray.ts | 21 + .../types/src/codec/utils/compareMap.spec.ts | 81 + packages/types/src/codec/utils/compareMap.ts | 34 + packages/types/src/codec/utils/decodeU8a.ts | 27 + packages/types/src/codec/utils/index.ts | 7 + packages/types/src/index.spec.ts | 45 + packages/types/src/index.ts | 14 + packages/types/src/index.types.ts | 14 + packages/types/src/json/AccountIdVec.001.json | 16 + packages/types/src/json/EventRecord.001.json | 16 + packages/types/src/json/EventRecord.002.json | 16 + packages/types/src/json/EventRecord.003.json | 16 + .../types/src/json/ExtrinsicStatus.001.json | 10 + packages/types/src/json/Header.001.json | 13 + packages/types/src/json/Header.002.json | 20 + .../types/src/json/RuntimeVersion.001.json | 39 + packages/types/src/json/SignedBlock.001.json | 43 + packages/types/src/json/SignedBlock.002.json | 58 + .../types/src/json/StorageChangeSet.001.json | 16 + packages/types/src/json/json.d.ts | 10 + packages/types/src/primitive/Bool.spec.ts | 48 + packages/types/src/primitive/Bool.ts | 85 + packages/types/src/primitive/Bytes.spec.ts | 46 + packages/types/src/primitive/Bytes.ts | 74 + packages/types/src/primitive/Data.ts | 13 + packages/types/src/primitive/H160.ts | 19 + packages/types/src/primitive/H256.ts | 19 + packages/types/src/primitive/H512.ts | 18 + packages/types/src/primitive/I128.ts | 18 + packages/types/src/primitive/I16.ts | 18 + packages/types/src/primitive/I256.ts | 18 + packages/types/src/primitive/I32.ts | 18 + packages/types/src/primitive/I64.ts | 18 + packages/types/src/primitive/I8.ts | 18 + packages/types/src/primitive/Method.spec.ts | 53 + packages/types/src/primitive/Method.ts | 229 ++ packages/types/src/primitive/Moment.spec.ts | 55 + packages/types/src/primitive/Moment.ts | 127 + packages/types/src/primitive/Null.spec.ts | 19 + packages/types/src/primitive/Null.ts | 63 + packages/types/src/primitive/StorageData.ts | 13 + .../types/src/primitive/StorageKey.spec.ts | 35 + packages/types/src/primitive/StorageKey.ts | 93 + packages/types/src/primitive/Text.spec.ts | 61 + packages/types/src/primitive/Text.ts | 106 + packages/types/src/primitive/Type.spec.ts | 94 + packages/types/src/primitive/Type.ts | 232 ++ packages/types/src/primitive/U128.ts | 18 + packages/types/src/primitive/U16.ts | 18 + packages/types/src/primitive/U256.ts | 18 + packages/types/src/primitive/U32.ts | 18 + packages/types/src/primitive/U64.ts | 18 + packages/types/src/primitive/U8.ts | 18 + packages/types/src/primitive/USize.ts | 14 + packages/types/src/primitive/index.ts | 38 + .../types/src/rpc/ChainProperties.spec.ts | 31 + packages/types/src/rpc/ChainProperties.ts | 26 + .../types/src/rpc/ExtrinsicStatus.spec.ts | 22 + packages/types/src/rpc/ExtrinsicStatus.ts | 184 + packages/types/src/rpc/Health.ts | 48 + packages/types/src/rpc/Json.ts | 77 + packages/types/src/rpc/NetworkState.ts | 13 + packages/types/src/rpc/PeerInfo.ts | 68 + packages/types/src/rpc/PendingExtrinsics.ts | 13 + packages/types/src/rpc/RuntimeVersion.spec.ts | 31 + packages/types/src/rpc/RuntimeVersion.ts | 131 + packages/types/src/rpc/SignedBlock.spec.ts | 66 + packages/types/src/rpc/SignedBlock.ts | 50 + .../types/src/rpc/StorageChangeSet.spec.ts | 51 + packages/types/src/rpc/StorageChangeSet.ts | 45 + packages/types/src/rpc/index.ts | 20 + .../src/scripts/METHODS_STORAGE_SUBSTRATE.md | 26 + packages/types/src/scripts/MetadataMd.ts | 200 + .../types/src/scripts/MetadataMdWrapper.js | 17 + packages/types/src/type/AccountId.spec.ts | 102 + packages/types/src/type/AccountId.ts | 71 + packages/types/src/type/AccountIndex.spec.ts | 61 + packages/types/src/type/AccountIndex.ts | 112 + packages/types/src/type/AccountInfo.ts | 35 + packages/types/src/type/Address.spec.ts | 118 + packages/types/src/type/Address.ts | 139 + packages/types/src/type/Amount.ts | 13 + packages/types/src/type/AssetOf.ts | 13 + packages/types/src/type/AttestedCandidate.ts | 96 + packages/types/src/type/AuthorityId.ts | 11 + packages/types/src/type/Balance.ts | 21 + packages/types/src/type/BalanceLock.ts | 53 + packages/types/src/type/Bft.spec.ts | 35 + packages/types/src/type/Bft.ts | 72 + packages/types/src/type/Block.ts | 61 + packages/types/src/type/BlockNumber.ts | 13 + packages/types/src/type/CodeHash.ts | 14 + packages/types/src/type/Digest.spec.ts | 22 + packages/types/src/type/Digest.ts | 167 + packages/types/src/type/Event.ts | 182 + packages/types/src/type/EventRecord.spec.ts | 67 + packages/types/src/type/EventRecord.ts | 96 + packages/types/src/type/Exposure.ts | 45 + packages/types/src/type/Extrinsic.spec.ts | 48 + packages/types/src/type/Extrinsic.ts | 204 ++ packages/types/src/type/ExtrinsicEra.ts | 34 + packages/types/src/type/ExtrinsicSignature.ts | 165 + packages/types/src/type/Extrinsics.ts | 14 + packages/types/src/type/Gas.ts | 13 + packages/types/src/type/Hash.ts | 15 + packages/types/src/type/Header.spec.ts | 42 + packages/types/src/type/Header.ts | 129 + packages/types/src/type/IndividualExposure.ts | 36 + .../types/src/type/InherentOfflineReport.ts | 13 + packages/types/src/type/Justification.spec.ts | 39 + packages/types/src/type/Justification.ts | 64 + packages/types/src/type/Key.ts | 14 + packages/types/src/type/KeyValue.spec.ts | 60 + packages/types/src/type/KeyValue.ts | 79 + packages/types/src/type/LockIdentifier.ts | 16 + packages/types/src/type/LockPeriods.ts | 13 + .../types/src/type/MisbehaviorReport.spec.ts | 47 + packages/types/src/type/MisbehaviorReport.ts | 209 ++ .../types/src/type/NewAccountOutcome.spec.ts | 13 + packages/types/src/type/NewAccountOutcome.ts | 21 + packages/types/src/type/Nonce.ts | 15 + packages/types/src/type/NonceCompact.ts | 15 + packages/types/src/type/Origin.spec.ts | 13 + packages/types/src/type/Origin.ts | 19 + packages/types/src/type/ParaId.ts | 13 + packages/types/src/type/Perbill.ts | 15 + packages/types/src/type/Permill.ts | 15 + packages/types/src/type/PrefabWasmModule.ts | 57 + packages/types/src/type/PropIndex.ts | 14 + packages/types/src/type/Proposal.ts | 13 + packages/types/src/type/ProposalIndex.ts | 14 + packages/types/src/type/ReferendumIndex.ts | 14 + packages/types/src/type/ReferendumInfo.ts | 61 + packages/types/src/type/RewardDestination.ts | 23 + packages/types/src/type/Schedule.ts | 91 + packages/types/src/type/SeedOf.ts | 13 + packages/types/src/type/SessionKey.ts | 14 + packages/types/src/type/Signature.ts | 30 + packages/types/src/type/SignaturePayload.ts | 106 + packages/types/src/type/StakingLedger.ts | 55 + .../types/src/type/StoredPendingChange.ts | 75 + packages/types/src/type/UnlockChunk.ts | 36 + .../types/src/type/ValidatorPrefs.spec.ts | 23 + packages/types/src/type/ValidatorPrefs.ts | 43 + packages/types/src/type/VestingSchedule.ts | 33 + packages/types/src/type/Vote.ts | 42 + packages/types/src/type/VoteIndex.ts | 13 + packages/types/src/type/VoteThreshold.spec.ts | 19 + packages/types/src/type/VoteThreshold.ts | 20 + packages/types/src/type/Votes.spec.ts | 31 + packages/types/src/type/WithdrawReasons.ts | 41 + packages/types/src/type/index.ts | 73 + packages/types/src/types.ts | 129 + packages/util-crypto/LICENSE | 201 ++ packages/util-crypto/README.md | 21 + packages/util-crypto/package.json | 46 + packages/util-crypto/src/blake2/asHex.spec.ts | 46 + packages/util-crypto/src/blake2/asHex.ts | 27 + packages/util-crypto/src/blake2/asU8a.spec.ts | 46 + packages/util-crypto/src/blake2/asU8a.ts | 29 + packages/util-crypto/src/blake2/index.ts | 10 + packages/util-crypto/src/index.ts | 34 + packages/util-crypto/src/keccak/asHex.spec.ts | 22 + packages/util-crypto/src/keccak/asHex.ts | 27 + packages/util-crypto/src/keccak/asU8a.spec.ts | 41 + packages/util-crypto/src/keccak/asU8a.ts | 29 + packages/util-crypto/src/keccak/index.ts | 10 + .../util-crypto/src/key/DeriveJunction.ts | 85 + .../util-crypto/src/key/extractPath.spec.ts | 53 + packages/util-crypto/src/key/extractPath.ts | 37 + .../util-crypto/src/key/extractSuri.spec.ts | 137 + packages/util-crypto/src/key/extractSuri.ts | 34 + packages/util-crypto/src/key/fromPath.ts | 19 + packages/util-crypto/src/key/hdkdEd25519.ts | 19 + packages/util-crypto/src/key/hdkdSr25519.ts | 15 + packages/util-crypto/src/key/index.ts | 12 + .../util-crypto/src/mnemonic/generate.spec.ts | 35 + packages/util-crypto/src/mnemonic/generate.ts | 37 + packages/util-crypto/src/mnemonic/index.ts | 15 + .../src/mnemonic/toEntropy.spec.ts | 21 + .../util-crypto/src/mnemonic/toEntropy.ts | 19 + .../src/mnemonic/toMiniSecret.spec.ts | 32 + .../util-crypto/src/mnemonic/toMiniSecret.ts | 25 + .../util-crypto/src/mnemonic/toSeed.spec.ts | 15 + packages/util-crypto/src/mnemonic/toSeed.ts | 30 + .../util-crypto/src/mnemonic/validate.spec.ts | 24 + packages/util-crypto/src/mnemonic/validate.ts | 27 + packages/util-crypto/src/nacl/decrypt.spec.ts | 25 + packages/util-crypto/src/nacl/decrypt.ts | 23 + packages/util-crypto/src/nacl/deriveHard.ts | 15 + packages/util-crypto/src/nacl/encrypt.spec.ts | 19 + packages/util-crypto/src/nacl/encrypt.ts | 33 + packages/util-crypto/src/nacl/index.ts | 16 + .../src/nacl/keypair/fromRandom.spec.ts | 27 + .../src/nacl/keypair/fromRandom.ts | 25 + .../src/nacl/keypair/fromSecret.spec.ts | 32 + .../src/nacl/keypair/fromSecret.ts | 25 + .../src/nacl/keypair/fromSeed.spec.ts | 39 + .../util-crypto/src/nacl/keypair/fromSeed.ts | 35 + .../src/nacl/keypair/fromString.spec.ts | 16 + .../src/nacl/keypair/fromString.ts | 33 + packages/util-crypto/src/nacl/sign.spec.ts | 27 + packages/util-crypto/src/nacl/sign.ts | 31 + packages/util-crypto/src/nacl/verify.spec.ts | 54 + packages/util-crypto/src/nacl/verify.ts | 26 + packages/util-crypto/src/polyfill.ts | 5 + packages/util-crypto/src/random/asHex.spec.ts | 37 + packages/util-crypto/src/random/asHex.ts | 27 + .../util-crypto/src/random/asNumber.spec.ts | 15 + packages/util-crypto/src/random/asNumber.ts | 30 + packages/util-crypto/src/random/asU8a.spec.ts | 35 + packages/util-crypto/src/random/asU8a.ts | 23 + packages/util-crypto/src/random/index.ts | 11 + .../util-crypto/src/schnorrkel/deriveHard.ts | 18 + .../util-crypto/src/schnorrkel/deriveSoft.ts | 18 + packages/util-crypto/src/schnorrkel/index.ts | 11 + .../src/schnorrkel/keypair/fromSeed.spec.ts | 39 + .../src/schnorrkel/keypair/fromSeed.ts | 21 + .../src/schnorrkel/keypair/fromU8a.ts | 15 + .../src/schnorrkel/keypair/testing.ts | 155 + .../src/schnorrkel/keypair/toU8a.ts | 11 + packages/util-crypto/src/schnorrkel/sign.ts | 21 + .../src/schnorrkel/signVerify.spec.ts | 32 + packages/util-crypto/src/schnorrkel/verify.ts | 15 + packages/util-crypto/src/secp256k1/index.ts | 5 + packages/util-crypto/src/secp256k1/recover.ts | 16 + packages/util-crypto/src/sha512/asU8a.spec.ts | 25 + packages/util-crypto/src/sha512/asU8a.ts | 26 + packages/util-crypto/src/sha512/index.ts | 9 + packages/util-crypto/src/types.ts | 15 + packages/util-crypto/src/xxhash/asHex.spec.ts | 31 + packages/util-crypto/src/xxhash/asHex.ts | 27 + packages/util-crypto/src/xxhash/asU8a.spec.ts | 38 + packages/util-crypto/src/xxhash/asU8a.ts | 38 + packages/util-crypto/src/xxhash/index.ts | 10 + .../util-crypto/src/xxhash/xxhash64/.nodoc | 0 .../src/xxhash/xxhash64/asBn.spec.ts | 13 + .../util-crypto/src/xxhash/xxhash64/asBn.ts | 28 + .../src/xxhash/xxhash64/asHex.spec.ts | 13 + .../util-crypto/src/xxhash/xxhash64/asHex.ts | 27 + .../src/xxhash/xxhash64/asRaw.spec.ts | 13 + .../util-crypto/src/xxhash/xxhash64/asRaw.ts | 23 + .../src/xxhash/xxhash64/asValue.spec.ts | 25 + .../src/xxhash/xxhash64/asValue.ts | 33 + .../util-crypto/src/xxhash/xxhash64/index.ts | 16 + packages/util/LICENSE | 201 ++ packages/util/README.md | 21 + packages/util/package.json | 41 + packages/util/src/array/filter.spec.ts | 19 + packages/util/src/array/filter.ts | 27 + packages/util/src/array/index.ts | 9 + packages/util/src/assert.spec.ts | 35 + packages/util/src/assert.ts | 38 + .../util/src/assertSingletonPackage.spec.ts | 21 + packages/util/src/assertSingletonPackage.ts | 25 + packages/util/src/bn/fromHex.spec.ts | 15 + packages/util/src/bn/fromHex.ts | 12 + packages/util/src/bn/index.ts | 12 + packages/util/src/bn/toBn.spec.ts | 27 + packages/util/src/bn/toBn.ts | 31 + packages/util/src/bn/toHex.spec.ts | 53 + packages/util/src/bn/toHex.ts | 49 + packages/util/src/bn/toU8a.spec.ts | 65 + packages/util/src/bn/toU8a.ts | 63 + packages/util/src/buffer/index.ts | 9 + packages/util/src/buffer/toU8a.spec.ts | 19 + packages/util/src/buffer/toU8a.ts | 25 + packages/util/src/compact/addLength.spec.ts | 13 + packages/util/src/compact/addLength.ts | 25 + packages/util/src/compact/defaults.ts | 7 + packages/util/src/compact/fromU8a.spec.ts | 48 + packages/util/src/compact/fromU8a.ts | 45 + packages/util/src/compact/index.ts | 29 + packages/util/src/compact/stripLength.spec.ts | 16 + packages/util/src/compact/stripLength.ts | 30 + packages/util/src/compact/toU8a.spec.ts | 102 + packages/util/src/compact/toU8a.ts | 55 + packages/util/src/compact/types.ts | 5 + packages/util/src/ext/error.spec.ts | 99 + packages/util/src/ext/error.ts | 66 + packages/util/src/ext/index.ts | 9 + .../util/src/format/formatBalance.spec.ts | 121 + packages/util/src/format/formatBalance.ts | 87 + .../util/src/format/formatDecimal.spec.ts | 19 + packages/util/src/format/formatDecimal.ts | 13 + .../util/src/format/formatElapsed.spec.ts | 44 + packages/util/src/format/formatElapsed.ts | 43 + packages/util/src/format/formatNumber.ts | 22 + packages/util/src/format/index.ts | 9 + packages/util/src/format/si.ts | 47 + packages/util/src/format/types.ts | 10 + packages/util/src/hex/addPrefix.spec.ts | 31 + packages/util/src/hex/addPrefix.ts | 31 + packages/util/src/hex/fixLength.spec.ts | 43 + packages/util/src/hex/fixLength.ts | 43 + packages/util/src/hex/hasPrefix.spec.ts | 31 + packages/util/src/hex/hasPrefix.ts | 23 + packages/util/src/hex/index.ts | 15 + packages/util/src/hex/stripPrefix.spec.ts | 31 + packages/util/src/hex/stripPrefix.ts | 37 + packages/util/src/hex/toBn.spec.ts | 80 + packages/util/src/hex/toBn.ts | 59 + packages/util/src/hex/toNumber.spec.ts | 19 + packages/util/src/hex/toNumber.ts | 27 + packages/util/src/hex/toU8a.spec.ts | 35 + packages/util/src/hex/toU8a.ts | 46 + packages/util/src/index.spec.ts | 11 + packages/util/src/index.ts | 26 + packages/util/src/is/bn.spec.ts | 21 + packages/util/src/is/bn.ts | 24 + packages/util/src/is/boolean.spec.ts | 25 + packages/util/src/is/boolean.ts | 21 + packages/util/src/is/buffer.spec.ts | 19 + packages/util/src/is/buffer.ts | 23 + packages/util/src/is/error.spec.ts | 19 + packages/util/src/is/error.ts | 23 + packages/util/src/is/function.spec.ts | 19 + packages/util/src/is/function.ts | 21 + packages/util/src/is/hex.spec.ts | 63 + packages/util/src/is/hex.ts | 32 + packages/util/src/is/index.ts | 25 + packages/util/src/is/instanceOf.spec.ts | 37 + packages/util/src/is/instanceOf.ts | 21 + packages/util/src/is/ip.spec.ts | 49 + packages/util/src/is/ip.ts | 37 + packages/util/src/is/jsonObject.spec.ts | 50 + packages/util/src/is/jsonObject.ts | 47 + packages/util/src/is/null.spec.ts | 19 + packages/util/src/is/null.ts | 21 + packages/util/src/is/number.spec.ts | 19 + packages/util/src/is/number.ts | 21 + packages/util/src/is/object.spec.ts | 19 + packages/util/src/is/object.ts | 26 + packages/util/src/is/observable.spec.ts | 25 + packages/util/src/is/observable.ts | 28 + packages/util/src/is/string.spec.ts | 31 + packages/util/src/is/string.ts | 21 + packages/util/src/is/testChain.spec.ts | 24 + packages/util/src/is/testChain.ts | 13 + packages/util/src/is/u8a.spec.ts | 25 + packages/util/src/is/u8a.ts | 23 + packages/util/src/is/undefined.spec.ts | 19 + packages/util/src/is/undefined.ts | 21 + packages/util/src/logger.spec.ts | 158 + packages/util/src/logger.ts | 126 + packages/util/src/number/index.ts | 10 + packages/util/src/number/toHex.spec.ts | 43 + packages/util/src/number/toHex.ts | 34 + packages/util/src/number/toU8a.spec.ts | 43 + packages/util/src/number/toU8a.ts | 30 + packages/util/src/polyfill/fill.spec.ts | 22 + packages/util/src/polyfill/fill.ts | 49 + packages/util/src/polyfill/index.ts | 10 + packages/util/src/polyfill/padEnd.spec.ts | 24 + packages/util/src/polyfill/padEnd.ts | 15 + packages/util/src/polyfill/padStart.spec.ts | 24 + packages/util/src/polyfill/padStart.ts | 15 + packages/util/src/polyfill/setPrototypeOf.ts | 12 + .../util/src/polyfill/textDecoder.spec.ts | 29 + packages/util/src/polyfill/textDecoder.ts | 13 + .../util/src/polyfill/textEncoder.spec.ts | 29 + packages/util/src/polyfill/textEncoder.ts | 13 + packages/util/src/promisify.spec.ts | 49 + packages/util/src/promisify.ts | 32 + packages/util/src/string/camelCase.spec.ts | 13 + packages/util/src/string/camelCase.ts | 7 + packages/util/src/string/index.ts | 13 + packages/util/src/string/lowerFirst.spec.ts | 31 + packages/util/src/string/lowerFirst.ts | 25 + packages/util/src/string/shorten.spec.ts | 31 + packages/util/src/string/shorten.ts | 27 + packages/util/src/string/toU8a.spec.ts | 21 + packages/util/src/string/toU8a.ts | 45 + packages/util/src/string/upperFirst.spec.ts | 31 + packages/util/src/string/upperFirst.ts | 25 + packages/util/src/types.ts | 26 + packages/util/src/u8a/concat.spec.ts | 31 + packages/util/src/u8a/concat.ts | 36 + packages/util/src/u8a/fixLength.spec.ts | 33 + packages/util/src/u8a/fixLength.ts | 41 + packages/util/src/u8a/index.ts | 15 + packages/util/src/u8a/toBn.spec.ts | 93 + packages/util/src/u8a/toBn.ts | 37 + packages/util/src/u8a/toBuffer.spec.ts | 19 + packages/util/src/u8a/toBuffer.ts | 26 + packages/util/src/u8a/toHex.spec.ts | 61 + packages/util/src/u8a/toHex.ts | 44 + packages/util/src/u8a/toString.spec.ts | 27 + packages/util/src/u8a/toString.ts | 41 + packages/util/src/u8a/toU8a.spec.ts | 51 + packages/util/src/u8a/toU8a.ts | 47 + tsconfig.json | 40 + tslint.json | 9 + typedoc.js | 16 + 688 files changed, 51707 insertions(+) create mode 100644 .123trigger create mode 100644 .babelrc.js create mode 100644 .codeclimate.yml create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .nvmrc create mode 100644 .travis.yml create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 babel.config.js create mode 100644 jest.config.js create mode 100644 lerna.json create mode 100644 package.json create mode 100644 packages/api-derive/LICENSE create mode 100644 packages/api-derive/README.md create mode 100644 packages/api-derive/package.json create mode 100644 packages/api-derive/src/accounts/idAndIndex.ts create mode 100644 packages/api-derive/src/accounts/idToIndex.ts create mode 100644 packages/api-derive/src/accounts/index.ts create mode 100644 packages/api-derive/src/accounts/indexToId.ts create mode 100644 packages/api-derive/src/accounts/indexes.ts create mode 100644 packages/api-derive/src/balances/fees.ts create mode 100644 packages/api-derive/src/balances/index.ts create mode 100644 packages/api-derive/src/balances/validatingBalance.ts create mode 100644 packages/api-derive/src/balances/validatingBalances.ts create mode 100644 packages/api-derive/src/balances/votingBalance.ts create mode 100644 packages/api-derive/src/balances/votingBalances.ts create mode 100644 packages/api-derive/src/balances/votingBalancesNominatorsFor.ts create mode 100644 packages/api-derive/src/chain/bestNumber.ts create mode 100644 packages/api-derive/src/chain/bestNumberFinalized.ts create mode 100644 packages/api-derive/src/chain/bestNumberLag.ts create mode 100644 packages/api-derive/src/chain/getHeader.ts create mode 100644 packages/api-derive/src/chain/index.ts create mode 100644 packages/api-derive/src/chain/subscribeNewHead.ts create mode 100644 packages/api-derive/src/democracy/index.ts create mode 100644 packages/api-derive/src/democracy/referendumInfo.ts create mode 100644 packages/api-derive/src/democracy/referendumInfos.ts create mode 100644 packages/api-derive/src/democracy/referendumVotesFor.ts create mode 100644 packages/api-derive/src/democracy/referendums.ts create mode 100644 packages/api-derive/src/democracy/votes.ts create mode 100644 packages/api-derive/src/index.spec.ts create mode 100644 packages/api-derive/src/index.ts create mode 100644 packages/api-derive/src/session/eraLength.ts create mode 100644 packages/api-derive/src/session/eraProgress.ts create mode 100644 packages/api-derive/src/session/index.ts create mode 100644 packages/api-derive/src/session/sessionProgress.ts create mode 100644 packages/api-derive/src/staking/controllers.ts create mode 100644 packages/api-derive/src/staking/index.ts create mode 100644 packages/api-derive/src/staking/intentionsBalances.ts create mode 100644 packages/api-derive/src/types.ts create mode 100644 packages/api-derive/src/util/drr.spec.ts create mode 100644 packages/api-derive/src/util/drr.ts create mode 100644 packages/api-derive/test/e2e/promise.spec.ts create mode 100644 packages/api-derive/test/e2e/rx.spec.ts create mode 100644 packages/api/LICENSE create mode 100644 packages/api/README.md create mode 100644 packages/api/package.json create mode 100644 packages/api/src/Base.ts create mode 100644 packages/api/src/SubmittableExtrinsic.ts create mode 100644 packages/api/src/checkTypes.manual.ts create mode 100644 packages/api/src/index.ts create mode 100644 packages/api/src/promise/Api.ts create mode 100644 packages/api/src/promise/Combinator.ts create mode 100644 packages/api/src/promise/Combinators.spec.ts create mode 100644 packages/api/src/promise/index.ts create mode 100644 packages/api/src/promise/types.ts create mode 100644 packages/api/src/rx/Api.ts create mode 100644 packages/api/src/rx/index.ts create mode 100644 packages/api/src/rx/types.ts create mode 100644 packages/api/src/types.ts create mode 100644 packages/api/src/util/filterEvents.ts create mode 100644 packages/api/src/util/logging.ts create mode 100644 packages/api/test/data/erc20.json create mode 100644 packages/api/test/data/erc20.wasm create mode 100644 packages/api/test/e2e/promise-alex.spec.js create mode 100644 packages/api/test/e2e/promise-contract.spec.js create mode 100644 packages/api/test/e2e/promise-queries.spec.js create mode 100644 packages/api/test/e2e/promise-tx.spec.js create mode 100644 packages/api/test/e2e/rx-queries.spec.js create mode 100644 packages/api/test/e2e/rx-tx.spec.js create mode 100644 packages/api/test/util/SingleAccountSigner.ts create mode 100644 packages/keyring/LICENSE create mode 100644 packages/keyring/README.md create mode 100644 packages/keyring/package.json create mode 100644 packages/keyring/src/address/decode.spec.ts create mode 100644 packages/keyring/src/address/decode.ts create mode 100644 packages/keyring/src/address/defaults.ts create mode 100644 packages/keyring/src/address/encode.spec.ts create mode 100644 packages/keyring/src/address/encode.ts create mode 100644 packages/keyring/src/address/index.ts create mode 100644 packages/keyring/src/address/setPrefix.spec.ts create mode 100644 packages/keyring/src/address/setPrefix.ts create mode 100644 packages/keyring/src/address/sshash.ts create mode 100644 packages/keyring/src/address/types.ts create mode 100644 packages/keyring/src/defaults.ts create mode 100644 packages/keyring/src/index.spec.ts create mode 100644 packages/keyring/src/index.ts create mode 100644 packages/keyring/src/pair/decode.spec.ts create mode 100644 packages/keyring/src/pair/decode.ts create mode 100644 packages/keyring/src/pair/defaults.ts create mode 100644 packages/keyring/src/pair/encode.spec.ts create mode 100644 packages/keyring/src/pair/encode.ts create mode 100644 packages/keyring/src/pair/index.spec.ts create mode 100644 packages/keyring/src/pair/index.ts create mode 100644 packages/keyring/src/pair/nobody.ts create mode 100644 packages/keyring/src/pair/toJson.spec.ts create mode 100644 packages/keyring/src/pair/toJson.ts create mode 100644 packages/keyring/src/pair/types.ts create mode 100644 packages/keyring/src/pairs.ts create mode 100644 packages/keyring/src/suri.spec.ts create mode 100644 packages/keyring/src/testing.ts create mode 100644 packages/keyring/src/testingPairs.spec.ts create mode 100644 packages/keyring/src/testingPairs.ts create mode 100644 packages/keyring/src/types.ts create mode 100644 packages/rpc-core/LICENSE create mode 100644 packages/rpc-core/README.md create mode 100644 packages/rpc-core/package.json create mode 100644 packages/rpc-core/src/formatting.spec.ts create mode 100644 packages/rpc-core/src/index.spec.ts create mode 100644 packages/rpc-core/src/index.ts create mode 100644 packages/rpc-core/src/methodSend.spec.ts create mode 100644 packages/rpc-core/src/types.ts create mode 100644 packages/rpc-core/test/e2e/basics.test.js create mode 100644 packages/rpc-core/test/e2e/chain.test.js create mode 100644 packages/rpc-core/test/e2e/krummelanke.test.js create mode 100644 packages/rpc-core/test/e2e/state.test.js create mode 100644 packages/rpc-core/test/e2e/subscribe.test.js create mode 100644 packages/rpc-provider/LICENSE create mode 100644 packages/rpc-provider/README.md create mode 100644 packages/rpc-provider/package.json create mode 100644 packages/rpc-provider/src/coder/decodeResponse.spec.ts create mode 100644 packages/rpc-provider/src/coder/encodeJson.spec.ts create mode 100644 packages/rpc-provider/src/coder/encodeObject.spec.ts create mode 100644 packages/rpc-provider/src/coder/index.ts create mode 100644 packages/rpc-provider/src/defaults.ts create mode 100644 packages/rpc-provider/src/http/index.spec.js create mode 100644 packages/rpc-provider/src/http/index.ts create mode 100644 packages/rpc-provider/src/http/polyfill.spec.js create mode 100644 packages/rpc-provider/src/http/polyfill.ts create mode 100644 packages/rpc-provider/src/http/send.spec.js create mode 100644 packages/rpc-provider/src/http/types.ts create mode 100644 packages/rpc-provider/src/index.ts create mode 100644 packages/rpc-provider/src/mock/index.ts create mode 100644 packages/rpc-provider/src/mock/on.spec.ts create mode 100644 packages/rpc-provider/src/mock/send.spec.ts create mode 100644 packages/rpc-provider/src/mock/subscribe.spec.ts create mode 100644 packages/rpc-provider/src/mock/types.ts create mode 100644 packages/rpc-provider/src/mock/unsubscribe.spec.ts create mode 100644 packages/rpc-provider/src/types.ts create mode 100644 packages/rpc-provider/src/ws/Provider.ts create mode 100644 packages/rpc-provider/src/ws/connect.spec.js create mode 100644 packages/rpc-provider/src/ws/index.spec.js create mode 100644 packages/rpc-provider/src/ws/index.ts create mode 100644 packages/rpc-provider/src/ws/onMessageResult.spec.js create mode 100644 packages/rpc-provider/src/ws/onMessageSubscribe.spec.js create mode 100644 packages/rpc-provider/src/ws/onOpen.spec.js create mode 100644 packages/rpc-provider/src/ws/polyfill.spec.js create mode 100644 packages/rpc-provider/src/ws/polyfill.ts create mode 100644 packages/rpc-provider/src/ws/send.spec.js create mode 100644 packages/rpc-provider/src/ws/state.spec.js create mode 100644 packages/rpc-provider/src/ws/subscribe.spec.js create mode 100644 packages/rpc-provider/src/ws/unsubscribe.spec.js create mode 100644 packages/rpc-provider/test/mockHttp.ts create mode 100644 packages/rpc-provider/test/mockWs.ts create mode 100644 packages/rpc-rx/LICENSE create mode 100644 packages/rpc-rx/README.md create mode 100644 packages/rpc-rx/package.json create mode 100644 packages/rpc-rx/src/cached.spec.ts create mode 100644 packages/rpc-rx/src/index.spec.ts create mode 100644 packages/rpc-rx/src/index.ts create mode 100644 packages/rpc-rx/src/replay.spec.ts create mode 100644 packages/rpc-rx/src/types.ts create mode 100644 packages/rpc-rx/test/e2e/subscribe.test.js create mode 100644 packages/type-extrinsics/LICENSE create mode 100644 packages/type-extrinsics/README.md create mode 100644 packages/type-extrinsics/package.json create mode 100644 packages/type-extrinsics/src/fromMetadata.spec.ts create mode 100644 packages/type-extrinsics/src/fromMetadata.ts create mode 100644 packages/type-extrinsics/src/index.spec.ts create mode 100644 packages/type-extrinsics/src/index.ts create mode 100644 packages/type-extrinsics/src/static.ts create mode 100644 packages/type-extrinsics/src/utils/createUnchecked.ts create mode 100644 packages/type-extrinsics/test/e2e/transfer.spec.js create mode 100644 packages/type-jsonrpc/LICENSE create mode 100644 packages/type-jsonrpc/README.md create mode 100644 packages/type-jsonrpc/package.json create mode 100644 packages/type-jsonrpc/src/author.ts create mode 100644 packages/type-jsonrpc/src/chain.ts create mode 100644 packages/type-jsonrpc/src/create/method.ts create mode 100644 packages/type-jsonrpc/src/create/param.ts create mode 100644 packages/type-jsonrpc/src/index.spec.js create mode 100644 packages/type-jsonrpc/src/index.ts create mode 100644 packages/type-jsonrpc/src/state.ts create mode 100644 packages/type-jsonrpc/src/system.ts create mode 100644 packages/type-jsonrpc/src/types.ts create mode 100644 packages/type-storage/LICENSE create mode 100644 packages/type-storage/README.md create mode 100644 packages/type-storage/package.json create mode 100644 packages/type-storage/src/fromMetadata.spec.ts create mode 100644 packages/type-storage/src/fromMetadata.ts create mode 100644 packages/type-storage/src/index.ts create mode 100644 packages/type-storage/src/static.ts create mode 100644 packages/type-storage/src/storage.spec.ts create mode 100644 packages/type-storage/src/storage.ts create mode 100644 packages/type-storage/src/substrate.spec.ts create mode 100644 packages/type-storage/src/substrate.ts create mode 100644 packages/type-storage/src/types.ts create mode 100644 packages/type-storage/src/utils/createFunction.spec.ts create mode 100644 packages/type-storage/src/utils/createFunction.ts create mode 100644 packages/types/LICENSE create mode 100644 packages/types/README.md create mode 100644 packages/types/package.json create mode 100644 packages/types/src/ContractAbi.ts create mode 100644 packages/types/src/Metadata/MagicNumber.spec.ts create mode 100644 packages/types/src/Metadata/MagicNumber.ts create mode 100644 packages/types/src/Metadata/Metadata.ts create mode 100644 packages/types/src/Metadata/MetadataVersioned.ts create mode 100644 packages/types/src/Metadata/README.md create mode 100644 packages/types/src/Metadata/index.ts create mode 100644 packages/types/src/Metadata/static.ts create mode 100644 packages/types/src/Metadata/types.ts create mode 100644 packages/types/src/Metadata/util/flattenUniq.ts create mode 100644 packages/types/src/Metadata/util/index.ts create mode 100644 packages/types/src/Metadata/util/validateTypes.ts create mode 100644 packages/types/src/Metadata/v0/Calls.ts create mode 100644 packages/types/src/Metadata/v0/Events.ts create mode 100644 packages/types/src/Metadata/v0/Metadata.spec.ts create mode 100644 packages/types/src/Metadata/v0/Metadata.ts create mode 100644 packages/types/src/Metadata/v0/Modules.ts create mode 100644 packages/types/src/Metadata/v0/index.ts create mode 100644 packages/types/src/Metadata/v0/latest.polkadot.json create mode 100644 packages/types/src/Metadata/v0/latest.substrate.json create mode 100644 packages/types/src/Metadata/v0/static.ts create mode 100644 packages/types/src/Metadata/v1/Calls.ts create mode 100644 packages/types/src/Metadata/v1/Events.ts create mode 100644 packages/types/src/Metadata/v1/Metadata.spec.ts create mode 100644 packages/types/src/Metadata/v1/Metadata.ts create mode 100644 packages/types/src/Metadata/v1/Storage.ts create mode 100644 packages/types/src/Metadata/v1/index.ts create mode 100644 packages/types/src/Metadata/v1/latest.substrate.v0.json create mode 100644 packages/types/src/Metadata/v1/latest.substrate.v1.json create mode 100644 packages/types/src/Metadata/v1/static.ts create mode 100644 packages/types/src/Metadata/v1/toV0.ts create mode 100644 packages/types/src/Metadata/v2/Metadata.spec.ts create mode 100644 packages/types/src/Metadata/v2/Metadata.ts create mode 100644 packages/types/src/Metadata/v2/Storage.ts create mode 100644 packages/types/src/Metadata/v2/index.ts create mode 100644 packages/types/src/Metadata/v2/latest.substrate.v2.json create mode 100644 packages/types/src/Metadata/v2/static.ts create mode 100644 packages/types/src/Metadata/v2/toV1.ts create mode 100644 packages/types/src/Metadata/v3/Metadata.spec.ts create mode 100644 packages/types/src/Metadata/v3/Metadata.ts create mode 100644 packages/types/src/Metadata/v3/Storage.ts create mode 100644 packages/types/src/Metadata/v3/index.ts create mode 100644 packages/types/src/Metadata/v3/latest.substrate.v3.json create mode 100644 packages/types/src/Metadata/v3/static.ts create mode 100644 packages/types/src/Metadata/v3/toV2.ts create mode 100644 packages/types/src/classes.ts create mode 100644 packages/types/src/codec/AbstractInt.ts create mode 100644 packages/types/src/codec/Base.ts create mode 100644 packages/types/src/codec/Compact.spec.ts create mode 100644 packages/types/src/codec/Compact.ts create mode 100644 packages/types/src/codec/Enum.spec.ts create mode 100644 packages/types/src/codec/Enum.ts create mode 100644 packages/types/src/codec/EnumType.spec.ts create mode 100644 packages/types/src/codec/EnumType.ts create mode 100644 packages/types/src/codec/Int.spec.ts create mode 100644 packages/types/src/codec/Int.ts create mode 100644 packages/types/src/codec/Linkage.ts create mode 100644 packages/types/src/codec/Option.spec.ts create mode 100644 packages/types/src/codec/Option.ts create mode 100644 packages/types/src/codec/Set.spec.ts create mode 100644 packages/types/src/codec/Set.ts create mode 100644 packages/types/src/codec/Struct.spec.ts create mode 100644 packages/types/src/codec/Struct.ts create mode 100644 packages/types/src/codec/Tuple.spec.ts create mode 100644 packages/types/src/codec/Tuple.ts create mode 100644 packages/types/src/codec/TypeRegistry.spec.ts create mode 100644 packages/types/src/codec/U8a.spec.ts create mode 100644 packages/types/src/codec/U8a.ts create mode 100644 packages/types/src/codec/U8aFixed.spec.ts create mode 100644 packages/types/src/codec/U8aFixed.ts create mode 100644 packages/types/src/codec/UInt.spec.ts create mode 100644 packages/types/src/codec/UInt.ts create mode 100644 packages/types/src/codec/Vector.spec.ts create mode 100644 packages/types/src/codec/Vector.ts create mode 100644 packages/types/src/codec/createType.spec.ts create mode 100644 packages/types/src/codec/createType.ts create mode 100644 packages/types/src/codec/index.ts create mode 100644 packages/types/src/codec/typeRegistry.ts create mode 100644 packages/types/src/codec/utils/compareArray.spec.ts create mode 100644 packages/types/src/codec/utils/compareArray.ts create mode 100644 packages/types/src/codec/utils/compareMap.spec.ts create mode 100644 packages/types/src/codec/utils/compareMap.ts create mode 100644 packages/types/src/codec/utils/decodeU8a.ts create mode 100644 packages/types/src/codec/utils/index.ts create mode 100644 packages/types/src/index.spec.ts create mode 100644 packages/types/src/index.ts create mode 100644 packages/types/src/index.types.ts create mode 100644 packages/types/src/json/AccountIdVec.001.json create mode 100644 packages/types/src/json/EventRecord.001.json create mode 100644 packages/types/src/json/EventRecord.002.json create mode 100644 packages/types/src/json/EventRecord.003.json create mode 100644 packages/types/src/json/ExtrinsicStatus.001.json create mode 100644 packages/types/src/json/Header.001.json create mode 100644 packages/types/src/json/Header.002.json create mode 100644 packages/types/src/json/RuntimeVersion.001.json create mode 100644 packages/types/src/json/SignedBlock.001.json create mode 100644 packages/types/src/json/SignedBlock.002.json create mode 100644 packages/types/src/json/StorageChangeSet.001.json create mode 100644 packages/types/src/json/json.d.ts create mode 100644 packages/types/src/primitive/Bool.spec.ts create mode 100644 packages/types/src/primitive/Bool.ts create mode 100644 packages/types/src/primitive/Bytes.spec.ts create mode 100644 packages/types/src/primitive/Bytes.ts create mode 100644 packages/types/src/primitive/Data.ts create mode 100644 packages/types/src/primitive/H160.ts create mode 100644 packages/types/src/primitive/H256.ts create mode 100644 packages/types/src/primitive/H512.ts create mode 100644 packages/types/src/primitive/I128.ts create mode 100644 packages/types/src/primitive/I16.ts create mode 100644 packages/types/src/primitive/I256.ts create mode 100644 packages/types/src/primitive/I32.ts create mode 100644 packages/types/src/primitive/I64.ts create mode 100644 packages/types/src/primitive/I8.ts create mode 100644 packages/types/src/primitive/Method.spec.ts create mode 100644 packages/types/src/primitive/Method.ts create mode 100644 packages/types/src/primitive/Moment.spec.ts create mode 100644 packages/types/src/primitive/Moment.ts create mode 100644 packages/types/src/primitive/Null.spec.ts create mode 100644 packages/types/src/primitive/Null.ts create mode 100644 packages/types/src/primitive/StorageData.ts create mode 100644 packages/types/src/primitive/StorageKey.spec.ts create mode 100644 packages/types/src/primitive/StorageKey.ts create mode 100644 packages/types/src/primitive/Text.spec.ts create mode 100644 packages/types/src/primitive/Text.ts create mode 100644 packages/types/src/primitive/Type.spec.ts create mode 100644 packages/types/src/primitive/Type.ts create mode 100644 packages/types/src/primitive/U128.ts create mode 100644 packages/types/src/primitive/U16.ts create mode 100644 packages/types/src/primitive/U256.ts create mode 100644 packages/types/src/primitive/U32.ts create mode 100644 packages/types/src/primitive/U64.ts create mode 100644 packages/types/src/primitive/U8.ts create mode 100644 packages/types/src/primitive/USize.ts create mode 100644 packages/types/src/primitive/index.ts create mode 100644 packages/types/src/rpc/ChainProperties.spec.ts create mode 100644 packages/types/src/rpc/ChainProperties.ts create mode 100644 packages/types/src/rpc/ExtrinsicStatus.spec.ts create mode 100644 packages/types/src/rpc/ExtrinsicStatus.ts create mode 100644 packages/types/src/rpc/Health.ts create mode 100644 packages/types/src/rpc/Json.ts create mode 100644 packages/types/src/rpc/NetworkState.ts create mode 100644 packages/types/src/rpc/PeerInfo.ts create mode 100644 packages/types/src/rpc/PendingExtrinsics.ts create mode 100644 packages/types/src/rpc/RuntimeVersion.spec.ts create mode 100644 packages/types/src/rpc/RuntimeVersion.ts create mode 100644 packages/types/src/rpc/SignedBlock.spec.ts create mode 100644 packages/types/src/rpc/SignedBlock.ts create mode 100644 packages/types/src/rpc/StorageChangeSet.spec.ts create mode 100644 packages/types/src/rpc/StorageChangeSet.ts create mode 100644 packages/types/src/rpc/index.ts create mode 100644 packages/types/src/scripts/METHODS_STORAGE_SUBSTRATE.md create mode 100644 packages/types/src/scripts/MetadataMd.ts create mode 100644 packages/types/src/scripts/MetadataMdWrapper.js create mode 100644 packages/types/src/type/AccountId.spec.ts create mode 100644 packages/types/src/type/AccountId.ts create mode 100644 packages/types/src/type/AccountIndex.spec.ts create mode 100644 packages/types/src/type/AccountIndex.ts create mode 100644 packages/types/src/type/AccountInfo.ts create mode 100644 packages/types/src/type/Address.spec.ts create mode 100644 packages/types/src/type/Address.ts create mode 100644 packages/types/src/type/Amount.ts create mode 100644 packages/types/src/type/AssetOf.ts create mode 100644 packages/types/src/type/AttestedCandidate.ts create mode 100644 packages/types/src/type/AuthorityId.ts create mode 100644 packages/types/src/type/Balance.ts create mode 100644 packages/types/src/type/BalanceLock.ts create mode 100644 packages/types/src/type/Bft.spec.ts create mode 100644 packages/types/src/type/Bft.ts create mode 100644 packages/types/src/type/Block.ts create mode 100644 packages/types/src/type/BlockNumber.ts create mode 100644 packages/types/src/type/CodeHash.ts create mode 100644 packages/types/src/type/Digest.spec.ts create mode 100644 packages/types/src/type/Digest.ts create mode 100644 packages/types/src/type/Event.ts create mode 100644 packages/types/src/type/EventRecord.spec.ts create mode 100644 packages/types/src/type/EventRecord.ts create mode 100644 packages/types/src/type/Exposure.ts create mode 100644 packages/types/src/type/Extrinsic.spec.ts create mode 100644 packages/types/src/type/Extrinsic.ts create mode 100644 packages/types/src/type/ExtrinsicEra.ts create mode 100644 packages/types/src/type/ExtrinsicSignature.ts create mode 100644 packages/types/src/type/Extrinsics.ts create mode 100644 packages/types/src/type/Gas.ts create mode 100644 packages/types/src/type/Hash.ts create mode 100644 packages/types/src/type/Header.spec.ts create mode 100644 packages/types/src/type/Header.ts create mode 100644 packages/types/src/type/IndividualExposure.ts create mode 100644 packages/types/src/type/InherentOfflineReport.ts create mode 100644 packages/types/src/type/Justification.spec.ts create mode 100644 packages/types/src/type/Justification.ts create mode 100644 packages/types/src/type/Key.ts create mode 100644 packages/types/src/type/KeyValue.spec.ts create mode 100644 packages/types/src/type/KeyValue.ts create mode 100644 packages/types/src/type/LockIdentifier.ts create mode 100644 packages/types/src/type/LockPeriods.ts create mode 100644 packages/types/src/type/MisbehaviorReport.spec.ts create mode 100644 packages/types/src/type/MisbehaviorReport.ts create mode 100644 packages/types/src/type/NewAccountOutcome.spec.ts create mode 100644 packages/types/src/type/NewAccountOutcome.ts create mode 100644 packages/types/src/type/Nonce.ts create mode 100644 packages/types/src/type/NonceCompact.ts create mode 100644 packages/types/src/type/Origin.spec.ts create mode 100644 packages/types/src/type/Origin.ts create mode 100644 packages/types/src/type/ParaId.ts create mode 100644 packages/types/src/type/Perbill.ts create mode 100644 packages/types/src/type/Permill.ts create mode 100644 packages/types/src/type/PrefabWasmModule.ts create mode 100644 packages/types/src/type/PropIndex.ts create mode 100644 packages/types/src/type/Proposal.ts create mode 100644 packages/types/src/type/ProposalIndex.ts create mode 100644 packages/types/src/type/ReferendumIndex.ts create mode 100644 packages/types/src/type/ReferendumInfo.ts create mode 100644 packages/types/src/type/RewardDestination.ts create mode 100644 packages/types/src/type/Schedule.ts create mode 100644 packages/types/src/type/SeedOf.ts create mode 100644 packages/types/src/type/SessionKey.ts create mode 100644 packages/types/src/type/Signature.ts create mode 100644 packages/types/src/type/SignaturePayload.ts create mode 100644 packages/types/src/type/StakingLedger.ts create mode 100644 packages/types/src/type/StoredPendingChange.ts create mode 100644 packages/types/src/type/UnlockChunk.ts create mode 100644 packages/types/src/type/ValidatorPrefs.spec.ts create mode 100644 packages/types/src/type/ValidatorPrefs.ts create mode 100644 packages/types/src/type/VestingSchedule.ts create mode 100644 packages/types/src/type/Vote.ts create mode 100644 packages/types/src/type/VoteIndex.ts create mode 100644 packages/types/src/type/VoteThreshold.spec.ts create mode 100644 packages/types/src/type/VoteThreshold.ts create mode 100644 packages/types/src/type/Votes.spec.ts create mode 100644 packages/types/src/type/WithdrawReasons.ts create mode 100644 packages/types/src/type/index.ts create mode 100644 packages/types/src/types.ts create mode 100644 packages/util-crypto/LICENSE create mode 100644 packages/util-crypto/README.md create mode 100644 packages/util-crypto/package.json create mode 100644 packages/util-crypto/src/blake2/asHex.spec.ts create mode 100644 packages/util-crypto/src/blake2/asHex.ts create mode 100644 packages/util-crypto/src/blake2/asU8a.spec.ts create mode 100644 packages/util-crypto/src/blake2/asU8a.ts create mode 100644 packages/util-crypto/src/blake2/index.ts create mode 100644 packages/util-crypto/src/index.ts create mode 100644 packages/util-crypto/src/keccak/asHex.spec.ts create mode 100644 packages/util-crypto/src/keccak/asHex.ts create mode 100644 packages/util-crypto/src/keccak/asU8a.spec.ts create mode 100644 packages/util-crypto/src/keccak/asU8a.ts create mode 100644 packages/util-crypto/src/keccak/index.ts create mode 100644 packages/util-crypto/src/key/DeriveJunction.ts create mode 100644 packages/util-crypto/src/key/extractPath.spec.ts create mode 100644 packages/util-crypto/src/key/extractPath.ts create mode 100644 packages/util-crypto/src/key/extractSuri.spec.ts create mode 100644 packages/util-crypto/src/key/extractSuri.ts create mode 100644 packages/util-crypto/src/key/fromPath.ts create mode 100644 packages/util-crypto/src/key/hdkdEd25519.ts create mode 100644 packages/util-crypto/src/key/hdkdSr25519.ts create mode 100644 packages/util-crypto/src/key/index.ts create mode 100644 packages/util-crypto/src/mnemonic/generate.spec.ts create mode 100644 packages/util-crypto/src/mnemonic/generate.ts create mode 100644 packages/util-crypto/src/mnemonic/index.ts create mode 100644 packages/util-crypto/src/mnemonic/toEntropy.spec.ts create mode 100644 packages/util-crypto/src/mnemonic/toEntropy.ts create mode 100644 packages/util-crypto/src/mnemonic/toMiniSecret.spec.ts create mode 100644 packages/util-crypto/src/mnemonic/toMiniSecret.ts create mode 100644 packages/util-crypto/src/mnemonic/toSeed.spec.ts create mode 100644 packages/util-crypto/src/mnemonic/toSeed.ts create mode 100644 packages/util-crypto/src/mnemonic/validate.spec.ts create mode 100644 packages/util-crypto/src/mnemonic/validate.ts create mode 100644 packages/util-crypto/src/nacl/decrypt.spec.ts create mode 100644 packages/util-crypto/src/nacl/decrypt.ts create mode 100644 packages/util-crypto/src/nacl/deriveHard.ts create mode 100644 packages/util-crypto/src/nacl/encrypt.spec.ts create mode 100644 packages/util-crypto/src/nacl/encrypt.ts create mode 100644 packages/util-crypto/src/nacl/index.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromRandom.spec.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromRandom.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromSecret.spec.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromSecret.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromSeed.spec.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromSeed.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromString.spec.ts create mode 100644 packages/util-crypto/src/nacl/keypair/fromString.ts create mode 100644 packages/util-crypto/src/nacl/sign.spec.ts create mode 100644 packages/util-crypto/src/nacl/sign.ts create mode 100644 packages/util-crypto/src/nacl/verify.spec.ts create mode 100644 packages/util-crypto/src/nacl/verify.ts create mode 100644 packages/util-crypto/src/polyfill.ts create mode 100644 packages/util-crypto/src/random/asHex.spec.ts create mode 100644 packages/util-crypto/src/random/asHex.ts create mode 100644 packages/util-crypto/src/random/asNumber.spec.ts create mode 100644 packages/util-crypto/src/random/asNumber.ts create mode 100644 packages/util-crypto/src/random/asU8a.spec.ts create mode 100644 packages/util-crypto/src/random/asU8a.ts create mode 100644 packages/util-crypto/src/random/index.ts create mode 100644 packages/util-crypto/src/schnorrkel/deriveHard.ts create mode 100644 packages/util-crypto/src/schnorrkel/deriveSoft.ts create mode 100644 packages/util-crypto/src/schnorrkel/index.ts create mode 100644 packages/util-crypto/src/schnorrkel/keypair/fromSeed.spec.ts create mode 100644 packages/util-crypto/src/schnorrkel/keypair/fromSeed.ts create mode 100644 packages/util-crypto/src/schnorrkel/keypair/fromU8a.ts create mode 100644 packages/util-crypto/src/schnorrkel/keypair/testing.ts create mode 100644 packages/util-crypto/src/schnorrkel/keypair/toU8a.ts create mode 100644 packages/util-crypto/src/schnorrkel/sign.ts create mode 100644 packages/util-crypto/src/schnorrkel/signVerify.spec.ts create mode 100644 packages/util-crypto/src/schnorrkel/verify.ts create mode 100644 packages/util-crypto/src/secp256k1/index.ts create mode 100644 packages/util-crypto/src/secp256k1/recover.ts create mode 100644 packages/util-crypto/src/sha512/asU8a.spec.ts create mode 100644 packages/util-crypto/src/sha512/asU8a.ts create mode 100644 packages/util-crypto/src/sha512/index.ts create mode 100644 packages/util-crypto/src/types.ts create mode 100644 packages/util-crypto/src/xxhash/asHex.spec.ts create mode 100644 packages/util-crypto/src/xxhash/asHex.ts create mode 100644 packages/util-crypto/src/xxhash/asU8a.spec.ts create mode 100644 packages/util-crypto/src/xxhash/asU8a.ts create mode 100644 packages/util-crypto/src/xxhash/index.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/.nodoc create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asBn.spec.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asBn.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asHex.spec.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asHex.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asRaw.spec.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asRaw.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asValue.spec.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/asValue.ts create mode 100644 packages/util-crypto/src/xxhash/xxhash64/index.ts create mode 100644 packages/util/LICENSE create mode 100644 packages/util/README.md create mode 100644 packages/util/package.json create mode 100644 packages/util/src/array/filter.spec.ts create mode 100644 packages/util/src/array/filter.ts create mode 100644 packages/util/src/array/index.ts create mode 100644 packages/util/src/assert.spec.ts create mode 100644 packages/util/src/assert.ts create mode 100644 packages/util/src/assertSingletonPackage.spec.ts create mode 100644 packages/util/src/assertSingletonPackage.ts create mode 100644 packages/util/src/bn/fromHex.spec.ts create mode 100644 packages/util/src/bn/fromHex.ts create mode 100644 packages/util/src/bn/index.ts create mode 100644 packages/util/src/bn/toBn.spec.ts create mode 100644 packages/util/src/bn/toBn.ts create mode 100644 packages/util/src/bn/toHex.spec.ts create mode 100644 packages/util/src/bn/toHex.ts create mode 100644 packages/util/src/bn/toU8a.spec.ts create mode 100644 packages/util/src/bn/toU8a.ts create mode 100644 packages/util/src/buffer/index.ts create mode 100644 packages/util/src/buffer/toU8a.spec.ts create mode 100644 packages/util/src/buffer/toU8a.ts create mode 100644 packages/util/src/compact/addLength.spec.ts create mode 100644 packages/util/src/compact/addLength.ts create mode 100644 packages/util/src/compact/defaults.ts create mode 100644 packages/util/src/compact/fromU8a.spec.ts create mode 100644 packages/util/src/compact/fromU8a.ts create mode 100644 packages/util/src/compact/index.ts create mode 100644 packages/util/src/compact/stripLength.spec.ts create mode 100644 packages/util/src/compact/stripLength.ts create mode 100644 packages/util/src/compact/toU8a.spec.ts create mode 100644 packages/util/src/compact/toU8a.ts create mode 100644 packages/util/src/compact/types.ts create mode 100644 packages/util/src/ext/error.spec.ts create mode 100644 packages/util/src/ext/error.ts create mode 100644 packages/util/src/ext/index.ts create mode 100644 packages/util/src/format/formatBalance.spec.ts create mode 100644 packages/util/src/format/formatBalance.ts create mode 100644 packages/util/src/format/formatDecimal.spec.ts create mode 100644 packages/util/src/format/formatDecimal.ts create mode 100644 packages/util/src/format/formatElapsed.spec.ts create mode 100644 packages/util/src/format/formatElapsed.ts create mode 100644 packages/util/src/format/formatNumber.ts create mode 100644 packages/util/src/format/index.ts create mode 100644 packages/util/src/format/si.ts create mode 100644 packages/util/src/format/types.ts create mode 100644 packages/util/src/hex/addPrefix.spec.ts create mode 100644 packages/util/src/hex/addPrefix.ts create mode 100644 packages/util/src/hex/fixLength.spec.ts create mode 100644 packages/util/src/hex/fixLength.ts create mode 100644 packages/util/src/hex/hasPrefix.spec.ts create mode 100644 packages/util/src/hex/hasPrefix.ts create mode 100644 packages/util/src/hex/index.ts create mode 100644 packages/util/src/hex/stripPrefix.spec.ts create mode 100644 packages/util/src/hex/stripPrefix.ts create mode 100644 packages/util/src/hex/toBn.spec.ts create mode 100644 packages/util/src/hex/toBn.ts create mode 100644 packages/util/src/hex/toNumber.spec.ts create mode 100644 packages/util/src/hex/toNumber.ts create mode 100644 packages/util/src/hex/toU8a.spec.ts create mode 100644 packages/util/src/hex/toU8a.ts create mode 100644 packages/util/src/index.spec.ts create mode 100644 packages/util/src/index.ts create mode 100644 packages/util/src/is/bn.spec.ts create mode 100644 packages/util/src/is/bn.ts create mode 100644 packages/util/src/is/boolean.spec.ts create mode 100644 packages/util/src/is/boolean.ts create mode 100644 packages/util/src/is/buffer.spec.ts create mode 100644 packages/util/src/is/buffer.ts create mode 100644 packages/util/src/is/error.spec.ts create mode 100644 packages/util/src/is/error.ts create mode 100644 packages/util/src/is/function.spec.ts create mode 100644 packages/util/src/is/function.ts create mode 100644 packages/util/src/is/hex.spec.ts create mode 100644 packages/util/src/is/hex.ts create mode 100644 packages/util/src/is/index.ts create mode 100644 packages/util/src/is/instanceOf.spec.ts create mode 100644 packages/util/src/is/instanceOf.ts create mode 100644 packages/util/src/is/ip.spec.ts create mode 100644 packages/util/src/is/ip.ts create mode 100644 packages/util/src/is/jsonObject.spec.ts create mode 100644 packages/util/src/is/jsonObject.ts create mode 100644 packages/util/src/is/null.spec.ts create mode 100644 packages/util/src/is/null.ts create mode 100644 packages/util/src/is/number.spec.ts create mode 100644 packages/util/src/is/number.ts create mode 100644 packages/util/src/is/object.spec.ts create mode 100644 packages/util/src/is/object.ts create mode 100644 packages/util/src/is/observable.spec.ts create mode 100644 packages/util/src/is/observable.ts create mode 100644 packages/util/src/is/string.spec.ts create mode 100644 packages/util/src/is/string.ts create mode 100644 packages/util/src/is/testChain.spec.ts create mode 100644 packages/util/src/is/testChain.ts create mode 100644 packages/util/src/is/u8a.spec.ts create mode 100644 packages/util/src/is/u8a.ts create mode 100644 packages/util/src/is/undefined.spec.ts create mode 100644 packages/util/src/is/undefined.ts create mode 100644 packages/util/src/logger.spec.ts create mode 100644 packages/util/src/logger.ts create mode 100644 packages/util/src/number/index.ts create mode 100644 packages/util/src/number/toHex.spec.ts create mode 100644 packages/util/src/number/toHex.ts create mode 100644 packages/util/src/number/toU8a.spec.ts create mode 100644 packages/util/src/number/toU8a.ts create mode 100644 packages/util/src/polyfill/fill.spec.ts create mode 100644 packages/util/src/polyfill/fill.ts create mode 100644 packages/util/src/polyfill/index.ts create mode 100644 packages/util/src/polyfill/padEnd.spec.ts create mode 100644 packages/util/src/polyfill/padEnd.ts create mode 100644 packages/util/src/polyfill/padStart.spec.ts create mode 100644 packages/util/src/polyfill/padStart.ts create mode 100644 packages/util/src/polyfill/setPrototypeOf.ts create mode 100644 packages/util/src/polyfill/textDecoder.spec.ts create mode 100644 packages/util/src/polyfill/textDecoder.ts create mode 100644 packages/util/src/polyfill/textEncoder.spec.ts create mode 100644 packages/util/src/polyfill/textEncoder.ts create mode 100644 packages/util/src/promisify.spec.ts create mode 100644 packages/util/src/promisify.ts create mode 100644 packages/util/src/string/camelCase.spec.ts create mode 100644 packages/util/src/string/camelCase.ts create mode 100644 packages/util/src/string/index.ts create mode 100644 packages/util/src/string/lowerFirst.spec.ts create mode 100644 packages/util/src/string/lowerFirst.ts create mode 100644 packages/util/src/string/shorten.spec.ts create mode 100644 packages/util/src/string/shorten.ts create mode 100644 packages/util/src/string/toU8a.spec.ts create mode 100644 packages/util/src/string/toU8a.ts create mode 100644 packages/util/src/string/upperFirst.spec.ts create mode 100644 packages/util/src/string/upperFirst.ts create mode 100644 packages/util/src/types.ts create mode 100644 packages/util/src/u8a/concat.spec.ts create mode 100644 packages/util/src/u8a/concat.ts create mode 100644 packages/util/src/u8a/fixLength.spec.ts create mode 100644 packages/util/src/u8a/fixLength.ts create mode 100644 packages/util/src/u8a/index.ts create mode 100644 packages/util/src/u8a/toBn.spec.ts create mode 100644 packages/util/src/u8a/toBn.ts create mode 100644 packages/util/src/u8a/toBuffer.spec.ts create mode 100644 packages/util/src/u8a/toBuffer.ts create mode 100644 packages/util/src/u8a/toHex.spec.ts create mode 100644 packages/util/src/u8a/toHex.ts create mode 100644 packages/util/src/u8a/toString.spec.ts create mode 100644 packages/util/src/u8a/toString.ts create mode 100644 packages/util/src/u8a/toU8a.spec.ts create mode 100644 packages/util/src/u8a/toU8a.ts create mode 100644 tsconfig.json create mode 100644 tslint.json create mode 100644 typedoc.js diff --git a/.123trigger b/.123trigger new file mode 100644 index 0000000000..7ed6ff82de --- /dev/null +++ b/.123trigger @@ -0,0 +1 @@ +5 diff --git a/.babelrc.js b/.babelrc.js new file mode 100644 index 0000000000..5188ea0422 --- /dev/null +++ b/.babelrc.js @@ -0,0 +1 @@ +module.exports = require('./babel.config.js'); diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000000..dd0a3cdb69 --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,6 @@ +exclude_patterns: +- "**/*.spec.js" +- "**/*.spec.ts" +- "**/*.test.js" +- "**/*.test.ts" +- "docs/**/*.js" diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..3c229b5dc1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true +[*] +indent_style=space +indent_size=2 +tab_width=2 +end_of_line=lf +charset=utf-8 +trim_trailing_whitespace=true +max_line_length=120 +insert_final_newline=true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..59a1afc00b --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +_book/ +build/ +build-docs/ +coverage/ +docs/.vuepress/dist/ +node_modules/ +tmp/ +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local +.npmrc +lerna-debug.log* +npm-debug.log* +yarn-debug.log* +yarn-error.log* +package-lock.json diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ + diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..f599e28b8a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +10 diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..f022701caa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +language: node_js +node_js: + - "10" +env: + - GH_PAGES_SRC="build-docs" +cache: + yarn: true + directories: + - node_modules +before_install: + - curl -o- -L https://yarnpkg.com/install.sh | bash + - export PATH=$HOME/.yarn/bin::$(yarn global bin):$PATH +script: + - yarn plugnet-dev-travis-build +deploy: + skip_cleanup: true + provider: script + script: yarn plugnet-dev-travis-deploy + on: + branch: master diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..922d5ada65 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,138 @@ +# 0.76.1 + +- Caching improvements (duplicate queries, no duplicate subscriptions) +- Experimental contract API +- Update @polkadot/keyring to enable Alice's stash account on dev chains +- Update @polkadot/util-crypto with smaller footprint + +# 0.75.1 + +- Start journey to 1.0 + +# 0.53.1 + +- Change spelling to US English as per substrate master (1.0-rc1). Breaking changes as a result: + - For extrinsic status results, if you have checked the type returns, i.e. `result.type === 'Finalised'` now check on the status for `result.status.isFinalized` or `result.status.isBroadcast`, ... (the `type` property is now accessible only on `result.status.type`) + - If using `subscribeFinalisedHeads` update this to `subscribeFinalizedHeads` (likewise `getFinalisedHead` should be updated to `getFinalizedHead` and `derive.bestNumberFinalized`) +- The underlying ss58 addess checksums have changed in the keyring along with the latest specs +- All examples have been updated with sr25519 addresses (with the new checksums) + +# 0.52.1 + +- Support queries to linked mapped storage (found in new staking interfaces) +- Add `derive.staking.controllers` to retrieve all active staking controllers +- Align types as per latest substrate master +- PeerInfo from system_peers does not have the index field anymore (dropped in substrate) +- Allow parsing of V3 metadata with DoubleMap support +- Check for single instances for api and types as they are loaded (assertSingletonPackage) + +# 0.51.1 + +- Support metadata V2 as per latest substrate master +- Update metadata with new types as per lastest substrate master + +# 0.50.1 + +- Lastest util-crypto (usage of WASM with JS fallbacks if not available) +- Update upstream @polkadot dependencies (for new crypto) + +# 0.49.1 + +- Fix large message signing on non-known nodes (default is now hashing, there has been enough time between upgrades) + +# 0.48.1 + +- Pull in new sr25519 capable keyring for dev nodes +- When using dev mode, it assumes that the node is the latest with derived sr25519 keys + +# 0.47.1 + +- Swap to publishing -beta.x on merge (non-breaking testing) + +# 0.46.1 + +- Extended type registration to now handle internal types as well. Additionally the built-in Extrinsic type can now we overridden with a custom version. +- Where `Extrinsic` and `Method` is used as types, considder importing `{ IMethod, IExtrinsic }` from `@polkadot/types/types`, especially in the cases where this is uased from a `SubmittableExtrinsic` +- The `typeRegistry` constant is now `getTypeRegistry()` as a function + +# 0.45.1 + +- Storage with option values now correctly return `Option` and is indicated as such in the documentation + +```js +// old +const ll = await api.query.session.lastLengthChange(); + +console.log('ll', ll /* BlockNumber */); // would be 0 if not set yet + +// new +const llo = await api.query.session.lastLengthChange(); + +console.log('llo', llo.unwrapOr('not set') /* Option */) +``` + +# 0.44.1 + +- Split primitives and types into seperate folders. This should not affect external use since the exports remain the same, however does have an impact where classes are referenced directly. e.g. + +```js +// old (affected) +import Method from '@polkadot/types/Method'; +import Signature from '@polkadot/types/Signature'; + +// new locations +import Method from '@polkadot/types/primitive/Method'; +import Signature from '@polkadot/types/type/Signature'; + +// unaffected +import { Method, Signature } from '@polkadot/types'; +``` + +# 0.43.1 + +- Intrduces support for the new keyring with sr25519 support in addition to ed25519. While this does not change the exposed API, it is considerred breaking since @polkadot/keuyring has interface changes. (Unless needed, don't rush the upgrade) + +# 0.42.1 + +- Support for substrate hash signing with implVersion >= 18 +- Changed signatures for Extrinsic signing, this should not have (much) of an impact since it is generally not used directly. SubmittableExtrinsic (as exposed by the API), supports the old-style use. + +# 0.41.1 + +- Support the V1 metadata specification from Substrate in addition to the currently testnet-active V0 version + +# 0.40.1 + +- The API interfaces now require a WS-compatible provider, e.g. subscription support is a must. Previously the HTTPProvider could be use (although it was very limited in the interactions). + +# 0.39.1 + +- The Promise API now returns a `Promise` instead of `UnsubFunction` when making subscriptions. + +# 0.38.1 + +Substrate has been updated with a breaking new transaction format where the Index/Nonce is now encoded as a Compact. This change is being rolled out to both Alexander (Polkadot testnet) as well as Charred Cherry (Substrate testnet) - transactions between old and new are not compatible. + +# 0.37.1 + +api-observable has been removed. This was only used in /apps and inconsistent with the api/rx and api/promise APIs. Future work will include derivates like was included in api-observable into the base. + +Tuples now return single types when only one type is available, i.e. `(AccountId)` would now resolve as `AccountId`. The extra type wrapper adds no benefit to users here. + +# 0.36.1 + +Api Promise has been updated in the way we deal with subscriptions. Previously a subscription returned `Promise` where the caller was to keep track of the id and use it in subsequent unsubscribes. Now any subscriptions return an unsubscribe/destroy function `(): void`, that is use for removing the subscriptions, e.g. + +```js +const unsubscribe = api.query.balance.freeBalance(Alice, (balance) => {...}); + +unsubscribe(); // here we destroy the subscription +``` + +# 0.35.1 + +Swapped to new metadata structures from Substrate. If the API is not working with your node, update Substrate to latest master branch. (Or 0.9.1 for Charred Cherry). Dropped support for old metadata as found as far back as BBQ Birch. + +# 0.34.1 + +Changed the send signature (for future expansion of eg. events) to return `result: { status: ExtrinsicStatus }` instead of `status: ExtrinsicStatus`. For most cases where only status `type` checks are used, i.e. `status.type === 'Finalised'` this should not be a breaking change. Deep inspection of the status object however will need to adapt. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..947952e851 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,45 @@ +# Contributing + +## What? + +Individuals making significant and valuable contributions are given commit-access to a project to contribute as they see fit. +A project is more like an open wiki than a standard guarded open source project. + +## Rules + +There are a few basic ground-rules for contributors (including the maintainer(s) of the project): + +1. **No `--force` pushes** or modifying the Git history in any way. If you need to rebase, ensure you do it in your own repo. +2. **Non-master branches**, prefixed with a short name moniker (e.g. `-`) must be used for ongoing work. +3. **All modifications** must be made in a **pull-request** to solicit feedback from other contributors. +4. A pull-request *must not be merged until CI* has finished successfully. + +#### Merging pull requests once CI is successful: +- A pull request with no large change to logic that is an urgent fix may be merged after a non-author contributor has reviewed it well. +- No PR should be merged until all reviews' comments are addressed. + +#### Reviewing pull requests: +When reviewing a pull request, the end-goal is to suggest useful changes to the author. Reviews should finish with approval unless there are issues that would result in: + +- Buggy behaviour. +- Undue maintenance burden. +- Breaking with house coding style. +- Pessimisation (i.e. reduction of speed as measured in the projects benchmarks). +- Feature reduction (i.e. it removes some aspect of functionality that a significant minority of users rely on). +- Uselessness (i.e. it does not strictly add a feature or fix a known issue). + +#### Reviews may not be used as an effective veto for a PR because: +- There exists a somewhat cleaner/better/faster way of accomplishing the same feature/fix. +- It does not fit well with some other contributors' longer-term vision for the project. + +## Releases + +Declaring formal releases remains the prerogative of the project maintainer(s). + +## Changes to this arrangement + +This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. + +## Heritage + +These contributing guidelines are modified from the "OPEN Open Source Project" guidelines for the Level project: [https://github.com/Level/community/blob/master/CONTRIBUTING.md](https://github.com/Level/community/blob/master/CONTRIBUTING.md) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..bb1aa5f899 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# @plugnet/api + +This library provides a clean wrapper around all the methods exposed by a Polkadot/Subtrate network client and defines all the types exposed by a node. For complete documentation around the classes, interfaces and their use, visit the [documentation portal](https://polkadot.js.org/api/). + +## tutorials + +Looking for tutorials to get started? Look at [examples](https://polkadot.js.org/api/examples/promise/) for guides on how to use the API to make queries and submit transactions. + +## overview + +The API is split up into a number of internal packages - + +- [@polkadot/api](packages/api/) The API library, providing both Promise and RxJS Observable-based interfaces. This is the main user-facing entry point. +- [@polkadot/api-derive](packages/api-derive/) Derived results that are injected into the API, allowing for combinations of various query results (only used internally and exposed on the Api instances via `api.derive.*`) +- [@polkadot/rpc-core](packages/rpc-core/) Wrapper around all [JSON-RPC methods](https://polkadot.js.org/api/METHODS_RPC.html) exposed by a Polkadot network client +- [@polkadot/rpc-provider](packages/rpc-provider/) Providers for connecting to nodes, including WebSockets and Http +- [@polkadot/rpc-rx](packages/rpc-rx/) A RxJs Observable wrapper around [@polkadot/rpc-provider](packages/rpc-provider) + +Type definitions for interfaces as exposed by Polkadot & Substrate clients - + +- [@polkadot/extrinsics](packages/type-extrinsics/) Base extrinsic definitions & codecs +- [@polkadot/jsonrpc](packages/type-jsonrpc/) Definitions for JSONRPC endpoints +- [@polkadot/storage](packages/type-storage/) Definitions for storage entries +- [@polkadot/types](packages/types/) Codecs for all Polkadot primitives + +## development + +Contributions are welcome! + +To start off, this repo (along with others in the [@polkadot](https://github.com/polkadot-js/) family) uses yarn workspaces to organise the code. As such, after cloning, its dependencies _should_ be installed via `yarn`, not via npm; the latter will result in broken dependencies. + +To get started - + +1. Clone the repo locally, via `git clone https://github.com/polkadot-js/api ` +2. Ensure that you have a recent version of Node.js, for development purposes [Node 10](https://nodejs.org/en/) is recommended. +3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >=1.10.1](https://yarnpkg.com/docs/install) is required. +4. Install the dependencies by running `yarn` +5. Build the everything via `yarn run build` +6. You can also launch the API Docs, via `yarn vuepress dev docs` +7. Access the docs via [http://localhost:8080](http://localhost:8080) diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000..23989e6606 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: '@plugnet/dev/config/babel' +}; diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000000..8db5888298 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,30 @@ +const config = require('@plugnet/dev/config/jest'); + +module.exports = Object.assign({}, config, { + moduleNameMapper: { + '@plugnet/api-derive(.*)$': '/packages/api-derive/src/$1', + '@plugnet/api(.*)$': '/packages/api/src/$1', + '@plugnet/rpc-(core|provider|rx)(.*)$': '/packages/rpc-$1/src/$2', + '@plugnet/extrinsics(.*)$': '/packages/type-extrinsics/src/$1', + '@plugnet/jsonrpc(.*)$': '/packages/type-jsonrpc/src/$1', + '@plugnet/storage(.*)$': '/packages/type-storage/src/$1', + '@plugnet/types(.*)$': '/packages/types/src/$1', + '@plugnet/keyring(.*)$': '/packages/keyring/src/$1', + '@plugnet/util(.*)$': '/packages/util/src/$1', + '@plugnet/util-crypto(.*)$': '/packages/util-crypto/src/$1' + }, + modulePathIgnorePatterns: [ + '/packages/api/build', + '/packages/api-derive/build', + '/packages/rpc-core/build', + '/packages/rpc-provider/build', + '/packages/rpc-rx/build', + '/packages/type-extrinsics/build', + '/packages/type-jsonrpc/build', + '/packages/type-storage/build/', + '/packages/types/build', + '/packages/keyring/build', + '/packages/util/build/', + '/packages/util-crypto/build' + ] +}); diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000000..6a2d23e207 --- /dev/null +++ b/lerna.json @@ -0,0 +1,13 @@ +{ + "npmClient": "yarn", + "useWorkspaces": true, + "command": { + "publish": { + "allowBranch": "master" + } + }, + "packages": [ + "packages/*" + ], + "version": "0.76.1" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000..e34163d9e3 --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "version": "0.76.1", + "private": true, + "engines": { + "node": ">=10.13.0", + "yarn": "^1.10.1" + }, + "workspaces": [ + "packages/*" + ], + "resolutions": { + "@vuepress/core": "1.0.0-alpha.44", + "babel-core": "^7.0.0-bridge.0", + "typescript": "^3.4.1", + "vuepress": "1.0.0-alpha.44" + }, + "scripts": { + "build": "plugnet-dev-build-ts && yarn run build:methodsdoc && plugnet-dev-build-docs", + "build:htmldoc": "yarn clean && typedoc --theme default --out docs/html", + "build:methodsdoc": "node packages/types/src/scripts/MetadataMdWrapper.js", + "check": "yarn lint", + "lint": "tslint --project . && tsc --noEmit --pretty", + "clean": "plugnet-dev-clean-build", + "postinstall": "plugnet-dev-yarn-only", + "test": "jest --coverage" + }, + "devDependencies": { + "@babel/core": "^7.4.3", + "@babel/register": "^7.4.0", + "@babel/runtime": "^7.4.3", + "@plugnet/dev": "file:///Users/moge/Workspace/centrality/plug/plugnet-js-dev/packages/dev/build", + "@polkadot/ts": "^0.1.56", + "gh-pages": "^2.0.1" + } +} diff --git a/packages/api-derive/LICENSE b/packages/api-derive/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/api-derive/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/api-derive/README.md b/packages/api-derive/README.md new file mode 100644 index 0000000000..a91ff67694 --- /dev/null +++ b/packages/api-derive/README.md @@ -0,0 +1,7 @@ +# @polkadot/api-derive + +Common functions used across Polkadot, derived from RPC calls and storage queries. + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/api-derive/package.json b/packages/api-derive/package.json new file mode 100644 index 0000000000..c780f7b9d8 --- /dev/null +++ b/packages/api-derive/package.json @@ -0,0 +1,39 @@ +{ + "name": "@plugnet/api-derive", + "version": "0.76.1", + "description": "Common functions used across Polkadot, derived from RPC calls and storage queries.", + "main": "index.js", + "keywords": [ + "Polkadot", + "Promise", + "RxJs" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/api-derive#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/api": "^0.76.1", + "@plugnet/types": "^0.76.1", + "@types/memoizee": "^0.4.2", + "memoizee": "^0.4.14" + } +} diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts new file mode 100644 index 0000000000..9ad37cf91f --- /dev/null +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, of } from 'rxjs'; +import { map, startWith } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { decodeAddress } from '@plugnet/keyring'; +import { AccountId, AccountIndex, Address } from '@plugnet/types'; +import { isU8a } from '@plugnet/util'; + +import { idToIndex } from './idToIndex'; +import { indexToId } from './indexToId'; +import { drr } from '../util/drr'; + +export type AccountIdAndIndex = [AccountId?, AccountIndex?]; + +export function idAndIndex (api: ApiInterface$Rx) { + return (address?: Address | AccountId | AccountIndex | string | null): Observable => { + try { + // yes, this can fail, don't care too much, catch will catch it + const decoded = isU8a(address) + ? address + : decodeAddress((address || '').toString()); + + if (decoded.length === 32) { + const accountId = new AccountId(decoded); + + return idToIndex(api)(accountId).pipe( + startWith(undefined), + map((accountIndex) => [accountId, accountIndex] as AccountIdAndIndex), + drr() + ); + } + + const accountIndex = new AccountIndex(decoded); + + return indexToId(api)(accountIndex).pipe( + startWith(undefined), + map((accountId) => [accountId, accountIndex] as AccountIdAndIndex), + drr() + ); + } catch (error) { + return of([undefined, undefined] as AccountIdAndIndex).pipe(drr()); + } + }; +} diff --git a/packages/api-derive/src/accounts/idToIndex.ts b/packages/api-derive/src/accounts/idToIndex.ts new file mode 100644 index 0000000000..ee6d6aa918 --- /dev/null +++ b/packages/api-derive/src/accounts/idToIndex.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { map, startWith } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, AccountIndex } from '@plugnet/types'; + +import { indexes, AccountIndexes } from './indexes'; +import { drr } from '../util/drr'; + +export function idToIndex (api: ApiInterface$Rx) { + return (accountId: AccountId | string): Observable => + indexes(api)() + .pipe( + startWith({}), + map((indexes: AccountIndexes) => (indexes || {})[accountId.toString()]), + drr() + ); +} diff --git a/packages/api-derive/src/accounts/index.ts b/packages/api-derive/src/accounts/index.ts new file mode 100644 index 0000000000..732bafdc94 --- /dev/null +++ b/packages/api-derive/src/accounts/index.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './idAndIndex'; +export * from './idToIndex'; +export * from './indexToId'; +export * from './indexes'; diff --git a/packages/api-derive/src/accounts/indexToId.ts b/packages/api-derive/src/accounts/indexToId.ts new file mode 100644 index 0000000000..83e26daa68 --- /dev/null +++ b/packages/api-derive/src/accounts/indexToId.ts @@ -0,0 +1,28 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { map, startWith } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { ENUMSET_SIZE } from '@plugnet/types/type/AccountIndex'; +import { AccountId, AccountIndex, Vector } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +export function indexToId (api: ApiInterface$Rx) { + return (_accountIndex: AccountIndex | string): Observable => { + const querySection = api.query.indices || api.query.balances; + const accountIndex = _accountIndex instanceof AccountIndex + ? _accountIndex + : new AccountIndex(_accountIndex); + + return (querySection.enumSet(accountIndex.div(ENUMSET_SIZE)) as Observable>) + .pipe( + startWith([]), + map((accounts) => (accounts || [])[accountIndex.mod(ENUMSET_SIZE).toNumber()]), + drr() + ); + }; + +} diff --git a/packages/api-derive/src/accounts/indexes.ts b/packages/api-derive/src/accounts/indexes.ts new file mode 100644 index 0000000000..9df264687a --- /dev/null +++ b/packages/api-derive/src/accounts/indexes.ts @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { combineLatest, Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { ENUMSET_SIZE } from '@plugnet/types/type/AccountIndex'; +import { AccountId, AccountIndex } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +export type AccountIndexes = { [index: string]: AccountIndex }; + +export function indexes (api: ApiInterface$Rx) { + return (): Observable => { + const querySection = api.query.indices || api.query.balances; + + return (querySection.nextEnumSet() as Observable) + .pipe( + map((next: AccountIndex) => { + const enumRange = [...Array(next.div(ENUMSET_SIZE).toNumber() + 1).keys()]; + + return enumRange; + }), + switchMap((enumRange) => combineLatest( + enumRange.map((index) => (querySection.enumSet(index) as Observable)) + )), + map((all: Array | undefined>) => + (all || []).reduce((result, list, outerIndex) => { + (list || []).forEach((accountId, innerIndex) => { + const index = (outerIndex * ENUMSET_SIZE.toNumber()) + innerIndex; + + result[accountId.toString()] = new AccountIndex(index); + }); + + return result; + }, {} as AccountIndexes)), + drr() + ); + }; +} diff --git a/packages/api-derive/src/balances/fees.ts b/packages/api-derive/src/balances/fees.ts new file mode 100644 index 0000000000..a9478b65fe --- /dev/null +++ b/packages/api-derive/src/balances/fees.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; + +import BN from 'bn.js'; +import { DerivedFees } from '../types'; +import { drr } from '../util/drr'; + +export function fees (api: ApiInterface$Rx) { + return (): Observable => { + return (combineLatest( + api.query.balances.creationFee(), + api.query.balances.existentialDeposit(), + api.query.fees + ? api.query.fees.transactionBaseFee() + : api.query.balances.transactionBaseFee(), + api.query.fees + ? api.query.fees.transactionByteFee() + : api.query.balances.transactionByteFee(), + api.query.balances.transferFee() + ) as any as Observable<[BN, BN, BN, BN, BN]>).pipe( + map(([creationFee, existentialDeposit, transactionBaseFee, transactionByteFee, transferFee]) => ({ + creationFee, + existentialDeposit, + transactionBaseFee, + transactionByteFee, + transferFee + })), + drr() + ) ; + }; +} diff --git a/packages/api-derive/src/balances/index.ts b/packages/api-derive/src/balances/index.ts new file mode 100644 index 0000000000..ff52076e80 --- /dev/null +++ b/packages/api-derive/src/balances/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './fees'; +export * from './validatingBalance'; +export * from './validatingBalances'; +export * from './votingBalance'; +export * from './votingBalances'; +export * from './votingBalancesNominatorsFor'; diff --git a/packages/api-derive/src/balances/validatingBalance.ts b/packages/api-derive/src/balances/validatingBalance.ts new file mode 100644 index 0000000000..ccb1e0fea1 --- /dev/null +++ b/packages/api-derive/src/balances/validatingBalance.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, AccountIndex, Address, Balance } from '@plugnet/types'; + +import { DerivedBalances } from '../types'; +import { drr } from '../util/drr'; +import { votingBalance } from './votingBalance'; +import { votingBalancesNominatorsFor } from './votingBalancesNominatorsFor'; + +export function validatingBalance (api: ApiInterface$Rx) { + return (address: AccountId | AccountIndex | Address | string): Observable => { + return combineLatest( + votingBalance(api)(address), + votingBalancesNominatorsFor(api)(address) + ).pipe( + map(([balance, nominators]) => { + const nominatedBalance = nominators.reduce( + (total: BN, nominatorBalance: DerivedBalances) => + total.add(nominatorBalance.votingBalance), + new BN(0) + ); + + return { + ...balance, + nominators, + nominatedBalance: new Balance(nominatedBalance), + stakingBalance: new Balance( + nominatedBalance.add(balance.votingBalance) + ) + }; + }), + drr() + ); + }; +} diff --git a/packages/api-derive/src/balances/validatingBalances.ts b/packages/api-derive/src/balances/validatingBalances.ts new file mode 100644 index 0000000000..af79165b40 --- /dev/null +++ b/packages/api-derive/src/balances/validatingBalances.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { combineLatest, Observable, of } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Address } from '@plugnet/types'; + +import { DerivedBalancesMap } from '../types'; +import { drr } from '../util/drr'; +import { validatingBalance } from './validatingBalance'; + +export function validatingBalances (api: ApiInterface$Rx) { + return (accountIds: Array): Observable => { + return !accountIds || !accountIds.length + ? of({}).pipe(drr()) + : combineLatest( + accountIds.map(validatingBalance(api)) + ).pipe( + map((result) => { + return result.reduce((balances, balance) => { + balances[balance.accountId.toString()] = balance; + + return balances; + }, {} as DerivedBalancesMap); + }), + drr() + ); + }; +} diff --git a/packages/api-derive/src/balances/votingBalance.ts b/packages/api-derive/src/balances/votingBalance.ts new file mode 100644 index 0000000000..317e63a6b7 --- /dev/null +++ b/packages/api-derive/src/balances/votingBalance.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { combineLatest, of, Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, AccountIndex, Address, Balance } from '@plugnet/types'; + +import { idAndIndex } from '../accounts/idAndIndex'; +import { DerivedBalances } from '../types'; +import { drr } from '../util/drr'; + +const EMPTY_ACCOUNT = new AccountId(new Uint8Array(32)); + +export function votingBalance (api: ApiInterface$Rx) { + return (address: AccountIndex | AccountId | Address | string): Observable => { + return idAndIndex(api)(address).pipe( + switchMap(([accountId]) => + (accountId + ? combineLatest( + of(accountId), + api.query.balances.freeBalance(accountId), + api.query.balances.reservedBalance(accountId) + ) + : of([undefined, undefined, undefined]) + ) as Observable<[AccountId?, Balance?, Balance?]> + ), + map(([accountId = EMPTY_ACCOUNT, freeBalance = new Balance(0), reservedBalance = new Balance(0)]) => { + return { + accountId, + freeBalance, + nominatedBalance: new Balance(0), + reservedBalance, + stakingBalance: new Balance(0), + votingBalance: new Balance( + freeBalance.add(reservedBalance) + ) + }; + }), + drr() + ); + }; +} diff --git a/packages/api-derive/src/balances/votingBalances.ts b/packages/api-derive/src/balances/votingBalances.ts new file mode 100644 index 0000000000..800da9823d --- /dev/null +++ b/packages/api-derive/src/balances/votingBalances.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { combineLatest, Observable, of } from 'rxjs'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, AccountIndex, Address } from '@plugnet/types'; + +import { DerivedBalances } from '../types'; +import { drr } from '../util/drr'; +import { votingBalance } from './votingBalance'; + +export function votingBalances (api: ApiInterface$Rx) { + return (addresses?: Array): Observable> => { + return !addresses || !addresses.length + ? of([]).pipe(drr()) + : combineLatest(...addresses.map(votingBalance(api))).pipe(drr()); + }; +} diff --git a/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts b/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts new file mode 100644 index 0000000000..fbcb920718 --- /dev/null +++ b/packages/api-derive/src/balances/votingBalancesNominatorsFor.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, AccountIndex, Address, Vector } from '@plugnet/types'; + +import { idAndIndex } from '../accounts/idAndIndex'; +import { DerivedBalances } from '../types'; +import { drr } from '../util/drr'; +import { votingBalances } from './votingBalances'; + +export function votingBalancesNominatorsFor (api: ApiInterface$Rx) { + return (address: AccountId | AccountIndex | Address | string): Observable> => { + return idAndIndex(api)(address).pipe( + switchMap(([accountId]) => + accountId + ? (api.query.staking.nominatorsFor(accountId) as Observable>) + : of([] as Array) + ), + switchMap(votingBalances(api)), + drr() + ); + }; +} diff --git a/packages/api-derive/src/chain/bestNumber.ts b/packages/api-derive/src/chain/bestNumber.ts new file mode 100644 index 0000000000..2e2acc18ed --- /dev/null +++ b/packages/api-derive/src/chain/bestNumber.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber, Header } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +/** + * @description Get the latest block number. + * @example + *
+ * + * ```javascript + * api.derive.chain.bestNumber((blockNumber) => { + * console.log(`the current best block is #${blockNumber}`); + * }); + * ``` + */ +export function bestNumber (api: ApiInterface$Rx) { + return (): Observable => + (api.rpc.chain.subscribeNewHead() as Observable
) + .pipe( + filter((header: Header) => header && !!header.blockNumber), + map(({ blockNumber }: Header) => blockNumber), + drr() + ); +} diff --git a/packages/api-derive/src/chain/bestNumberFinalized.ts b/packages/api-derive/src/chain/bestNumberFinalized.ts new file mode 100644 index 0000000000..744a3a45fe --- /dev/null +++ b/packages/api-derive/src/chain/bestNumberFinalized.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber, Header } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +/** + * @description Get the latest finalised block number. + * example + *
+ * + * ```javascript + * api.derive.chain.bestNumberFinalized((blockNumber) => { + * console.log(`the current finalised block is #${blockNumber}`); + * }); + * ``` + */ +export function bestNumberFinalized (api: ApiInterface$Rx) { + return (): Observable => + (api.rpc.chain.subscribeFinalizedHeads() as Observable
) + .pipe( + filter((header: Header) => header && !!header.blockNumber), + map(({ blockNumber }: Header) => blockNumber), + drr() + ); +} diff --git a/packages/api-derive/src/chain/bestNumberLag.ts b/packages/api-derive/src/chain/bestNumberLag.ts new file mode 100644 index 0000000000..efecf2e6aa --- /dev/null +++ b/packages/api-derive/src/chain/bestNumberLag.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, combineLatest } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber } from '@plugnet/types'; + +import { drr } from '../util/drr'; +import { bestNumber } from './bestNumber'; +import { bestNumberFinalized } from './bestNumberFinalized'; + +/** + * @description Calculates the lag between finalised head and best head + * @example + *
+ * + * ```javascript + * api.derive.chain.bestNumberLag((lag) => { + * console.log(`finalised is ${lag} blocks behind head`); + * }); + * ``` + */ +export function bestNumberLag (api: ApiInterface$Rx) { + return (): Observable => + combineLatest( + bestNumber(api)(), + bestNumberFinalized(api)() + ).pipe( + map(([bestNumber, bestNumberFinalized]) => + new BlockNumber(bestNumber.sub(bestNumberFinalized)) + ), + drr() + ); +} diff --git a/packages/api-derive/src/chain/getHeader.ts b/packages/api-derive/src/chain/getHeader.ts new file mode 100644 index 0000000000..9a6a9b7be4 --- /dev/null +++ b/packages/api-derive/src/chain/getHeader.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, combineLatest, of } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { Header, HeaderExtended } from '@plugnet/types'; + +import { drr } from '../util/drr'; +import { HeaderAndValidators } from './subscribeNewHead'; + +/** + * @description Get the a specific block header and extend it with the author + * @param hash: Uint8Array | string + * @example + *
+ * + * ```javascript + * const { author, blockNumber } = await api.derive.chain.getHeader('0x123...456'); + * + * console.log(`block #${blockNumber} was authored by ${author}`); + * ``` + */ +export function getHeader (api: ApiInterface$Rx) { + return (hash: Uint8Array | string): Observable => + // tslint:disable-next-line + (combineLatest( + api.rpc.chain.getHeader(hash) as Observable
, + api.query.session + ? api.query.session.validators.at(hash) + : of([]) + ) as Observable).pipe( + map(([header, validators]) => + new HeaderExtended(header, validators) + ), + catchError(() => + // where rpc.chain.getHeader throws, we will land here - it can happen that + // we supplied an invalid hash. (Due to defaults, storeage will have an + // empty value, so only the RPC is affected). So return undefined + // tslint:disable-next-line + of() as Observable + ), + drr() + ); +} diff --git a/packages/api-derive/src/chain/index.ts b/packages/api-derive/src/chain/index.ts new file mode 100644 index 0000000000..ddfc17ebb5 --- /dev/null +++ b/packages/api-derive/src/chain/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './bestNumber'; +export * from './bestNumberFinalized'; +export * from './bestNumberLag'; +export * from './getHeader'; +export * from './subscribeNewHead'; diff --git a/packages/api-derive/src/chain/subscribeNewHead.ts b/packages/api-derive/src/chain/subscribeNewHead.ts new file mode 100644 index 0000000000..94050ec4ec --- /dev/null +++ b/packages/api-derive/src/chain/subscribeNewHead.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, combineLatest, of } from 'rxjs'; +import { filter, map, switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Header, HeaderExtended } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +export type HeaderAndValidators = [Header, Array]; + +/** + * @description Subscribe to block headers and extend it with the author + * @example + *
+ * + * ```javascript + * api.derive.chain.subscribeNewHead(({ author, blockNumber }) => { + * console.log(`block #${blockNumber} was authored by ${author}`); + * }); + * ``` + */ +export function subscribeNewHead (api: ApiInterface$Rx) { + return (): Observable => + (api.rpc.chain.subscribeNewHead() as Observable
) + .pipe( + filter((header: Header) => + header && !!header.blockNumber + ), + switchMap((header: Header): Observable => + (combineLatest( + of(header), + // theoretically we could combine at the first call with session.validators(), however + // we make 100% sure we actually get the validators at a specific block so when these + // change at an era boundary, we have the previous values to ensure our indexes are correct + api.query.session + ? api.query.session.validators.at(header.hash) + : of([]) + ) as Observable) + ), + map(([header, validators]) => + new HeaderExtended(header, validators) + ), + drr() + ); +} diff --git a/packages/api-derive/src/democracy/index.ts b/packages/api-derive/src/democracy/index.ts new file mode 100644 index 0000000000..0728f9be6a --- /dev/null +++ b/packages/api-derive/src/democracy/index.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './referendumInfos'; +export * from './referendums'; +export * from './referendumVotesFor'; +export * from './votes'; diff --git a/packages/api-derive/src/democracy/referendumInfo.ts b/packages/api-derive/src/democracy/referendumInfo.ts new file mode 100644 index 0000000000..12bfaf7211 --- /dev/null +++ b/packages/api-derive/src/democracy/referendumInfo.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { Option, ReferendumInfo, ReferendumIndex } from '@plugnet/types'; +import { isNull } from '@plugnet/util'; + +import { drr } from '../util/drr'; + +/** + * @name ReferendumInfoExtended + * @description + * A [[ReferendumInfo]] with an additional `index` field + */ +export class ReferendumInfoExtended extends ReferendumInfo { + private _index: ReferendumIndex; + + constructor (value: ReferendumInfo | ReferendumInfoExtended, index?: BN | number) { + super(value); + + this._index = value instanceof ReferendumInfoExtended + ? value.index + : new ReferendumIndex(index); + } + + /** + * @description Convenience getter, returns the referendumIndex + */ + get index (): ReferendumIndex { + return this._index; + } + + /** + * @description Creates the JSON representation + */ + toJSON (): any { + return { + ...super.toJSON(), + index: this.index.toJSON() + }; + } +} + +export function referendumInfo (api: ApiInterface$Rx) { + return (index: BN | number): Observable> => { + return (api.query.democracy.referendumInfoOf(index) as Observable>) + .pipe( + map((optionInfo) => { + const info = optionInfo.unwrapOr(null); + + return new Option( + ReferendumInfoExtended, + isNull(info) + ? null + : new ReferendumInfoExtended(info, index) + ); + }), + drr() + ); + }; + +} diff --git a/packages/api-derive/src/democracy/referendumInfos.ts b/packages/api-derive/src/democracy/referendumInfos.ts new file mode 100644 index 0000000000..0ec889860f --- /dev/null +++ b/packages/api-derive/src/democracy/referendumInfos.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable, of } from 'rxjs'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { Option } from '@plugnet/types'; + +import { drr } from '../util/drr'; +import { referendumInfo, ReferendumInfoExtended } from './referendumInfo'; + +export function referendumInfos (api: ApiInterface$Rx) { + const referendumInfoOf = referendumInfo(api); + + return (ids: Array = []): Observable>> => { + return !ids || !ids.length + ? of([]).pipe(drr()) + : combineLatest( + ids.map((id) => referendumInfoOf(id)) + ).pipe( + drr() + ); + }; + +} diff --git a/packages/api-derive/src/democracy/referendumVotesFor.ts b/packages/api-derive/src/democracy/referendumVotesFor.ts new file mode 100644 index 0000000000..eb9d63ee56 --- /dev/null +++ b/packages/api-derive/src/democracy/referendumVotesFor.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Balance, Vector, Vote } from '@plugnet/types'; + +import { DerivedReferendumVote } from '../types'; +import { drr } from '../util/drr'; +import { votes } from './votes'; +import { votingBalances } from '../balances/votingBalances'; + +export function referendumVotesFor (api: ApiInterface$Rx) { + return (referendumId: BN | number): Observable> => + (api.query.democracy.votersFor(referendumId) as Observable>).pipe( + switchMap((votersFor) => + combineLatest( + of(votersFor), + votes(api)(referendumId as BN, votersFor), + votingBalances(api)(votersFor) + ) + ), + map(([votersFor, votes, balances]) => + votersFor.map((accountId, index): DerivedReferendumVote => ({ + accountId, + balance: balances[index].votingBalance || new Balance(0), + vote: votes[index] || new Vote(0) + })) + ), + drr() + ); + +} diff --git a/packages/api-derive/src/democracy/referendums.ts b/packages/api-derive/src/democracy/referendums.ts new file mode 100644 index 0000000000..8f554c656d --- /dev/null +++ b/packages/api-derive/src/democracy/referendums.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { combineLatest, Observable, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { Option, ReferendumIndex } from '@plugnet/types'; + +import { drr } from '../util/drr'; +import { ReferendumInfoExtended } from './referendumInfo'; +import { referendumInfos } from './referendumInfos'; + +export function referendums (api: ApiInterface$Rx) { + return (): Observable>> => + (combineLatest( + api.query.democracy.nextTally(), + api.query.democracy.referendumCount() + ) as Observable<[ReferendumIndex?, ReferendumIndex?]>).pipe( + switchMap(([nextTally, referendumCount]) => + referendumCount && nextTally && referendumCount.gt(nextTally) && referendumCount.gtn(0) + ? referendumInfos(api)( + [...Array(referendumCount.sub(nextTally).toNumber())].map((_, i) => + nextTally.addn(i) + ) + ) + : of([]) + ), + drr() + ); +} diff --git a/packages/api-derive/src/democracy/votes.ts b/packages/api-derive/src/democracy/votes.ts new file mode 100644 index 0000000000..4d3c4bc29f --- /dev/null +++ b/packages/api-derive/src/democracy/votes.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable, of } from 'rxjs'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Vote } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +export function votes (api: ApiInterface$Rx) { + return (referendumId: BN, accountIds: Array = []): Observable> => { + return !accountIds || !accountIds.length + ? of([]).pipe(drr()) + : combineLatest( + accountIds.map( + (accountId) => api.query.democracy.voteOf([referendumId, accountId]) as Observable + ) + ).pipe( + drr() + ); + }; +} diff --git a/packages/api-derive/src/index.spec.ts b/packages/api-derive/src/index.spec.ts new file mode 100644 index 0000000000..ff91c225e9 --- /dev/null +++ b/packages/api-derive/src/index.spec.ts @@ -0,0 +1,88 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, from } from 'rxjs'; +import ApiRx from '@plugnet/api/rx/Api'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import MockProvider from '@plugnet/rpc-provider/mock'; + +import { Derive } from '.'; + +const testFunction = (api: ApiInterface$Rx) => { + return < + Section extends keyof Derive, + Method extends keyof (typeof api.derive[Section]) + >(section: Section, method: Method, inputs: any[]) => { + describe(`derive.${section}.${method}`, () => { + it('should be a function', () => { + expect(typeof api.derive[section][method]).toBe('function'); + }); + + it('should return an Observable', () => { + expect((api.derive[section][method] as any)(...inputs)).toBeInstanceOf(Observable); + }); + }); + }; +}; + +describe('derive', () => { + describe('builtin', () => { + const api = new ApiRx(new MockProvider()); + + beforeAll((done) => { + api.isReady.subscribe(() => done()); + }); + + testFunction(api)('accounts', 'idAndIndex', []); + testFunction(api)('accounts', 'idToIndex', []); + testFunction(api)('accounts', 'indexes', []); + testFunction(api)('accounts', 'indexToId', []); + + testFunction(api)('balances', 'fees', []); + testFunction(api)('balances', 'validatingBalance', []); + testFunction(api)('balances', 'validatingBalances', []); + testFunction(api)('balances', 'votingBalance', []); + testFunction(api)('balances', 'votingBalances', []); + testFunction(api)('balances', 'votingBalancesNominatorsFor', []); + + testFunction(api)('chain', 'bestNumber', []); + testFunction(api)('chain', 'bestNumberFinalized', []); + + testFunction(api)('democracy', 'referendumInfos', []); + testFunction(api)('democracy', 'referendums', []); + testFunction(api)('democracy', 'referendumVotesFor', []); + testFunction(api)('democracy', 'votes', []); + + testFunction(api)('session', 'eraLength', []); + testFunction(api)('session', 'eraProgress', []); + testFunction(api)('session', 'sessionProgress', []); + }); + + describe('custom', () => { + const api = new ApiRx({ + derives: { + balances: { + fees: () => () => from(['a', 'b']) + }, + custom: { + test: () => () => from([1, 2, 3]) + } + }, + provider: new MockProvider() + }); + + beforeAll((done) => { + api.isReady.subscribe(() => done()); + }); + + // override + testFunction(api)('balances', 'fees', ['a', 'b']); + + // new + testFunction(api)('custom' as any, 'test', [1, 2, 3]); + + // existing + testFunction(api)('chain', 'bestNumber', []); + }); +}); diff --git a/packages/api-derive/src/index.ts b/packages/api-derive/src/index.ts new file mode 100644 index 0000000000..33852d6f2d --- /dev/null +++ b/packages/api-derive/src/index.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { ApiInterface$Rx } from '@plugnet/api/types'; + +import * as accounts from './accounts'; +import * as balances from './balances'; +import * as chain from './chain'; +import * as democracy from './democracy'; +import * as session from './session'; +import * as staking from './staking'; + +// Put all derived functions in an object, for easier Object.keys()-ing. +const functions = { accounts, balances, chain, democracy, session, staking }; + +/** + * T represents the section here (chain, balances...), and P represents + * the function name (bestNumber, sessionProgress...). + */ +type ReturnTypes any>> = { + [P in keyof T]: ReturnType +}; + +export interface DeriveCustomMethod { + (api: ApiInterface$Rx): (...args: Array) => Observable; +} + +export interface DeriveCustom { + [index: string]: { + [index: string]: DeriveCustomMethod + }; +} + +export interface Derive { + accounts: ReturnTypes; + balances: ReturnTypes; + chain: ReturnTypes; + democracy: ReturnTypes; + session: ReturnTypes; + staking: ReturnTypes; +} + +function injectFunctions (api: ApiInterface$Rx, derive: Derive, functions: DeriveCustom): Derive { + Object.keys(functions).forEach((sectionName: string) => { + const section = functions[sectionName as keyof Derive]; + const result = derive[sectionName as keyof Derive] = derive[sectionName as keyof Derive] || {}; + + Object.keys(section).forEach((methodName) => { + // @ts-ignore No idea how to make this work... + result[methodName as keyof section] = section[methodName as keyof section](api); + }); + }); + + return derive; +} + +export default function decorateDerive (api: ApiInterface$Rx, custom: DeriveCustom = {}): Derive { + const derive = {} as Derive; + + injectFunctions(api, derive, functions); + injectFunctions(api, derive, custom); + + return derive; +} diff --git a/packages/api-derive/src/session/eraLength.ts b/packages/api-derive/src/session/eraLength.ts new file mode 100644 index 0000000000..c339edc206 --- /dev/null +++ b/packages/api-derive/src/session/eraLength.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +export function eraLength (api: ApiInterface$Rx) { + return (): Observable => + (combineLatest( + api.query.session.sessionLength(), + api.query.staking.sessionsPerEra() + ) as Observable<[BlockNumber?, BlockNumber?]>).pipe( + map( + ([sessionLength, sessionsPerEra]) => + (sessionLength || new BN(1)).mul(sessionsPerEra || new BN(1)) + ), + drr() + ); +} diff --git a/packages/api-derive/src/session/eraProgress.ts b/packages/api-derive/src/session/eraProgress.ts new file mode 100644 index 0000000000..e2ec208fa7 --- /dev/null +++ b/packages/api-derive/src/session/eraProgress.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber } from '@plugnet/types'; + +import { sessionProgress } from './sessionProgress'; +import { drr } from '../util/drr'; + +export function eraProgress (api: ApiInterface$Rx) { + return (): Observable => + (combineLatest( + sessionProgress(api)(), + api.query.session.currentIndex(), + api.query.session.sessionLength(), + api.query.staking.lastEraLengthChange(), + api.query.staking.sessionsPerEra() + ) as Observable<[BN, BlockNumber?, BlockNumber?, BlockNumber?, BlockNumber?]>).pipe( + map( + ([sessionProgress, currentIndex, sessionLength, lastEraLengthChange, sessionsPerEra]) => + (currentIndex || new BN(0)) + .sub(lastEraLengthChange || new BN(0)) + .mod(sessionsPerEra || new BN(1)) + .mul(sessionLength || new BN(1)) + .add(sessionProgress || new BN(0) + ) + + ), + drr() + ); +} diff --git a/packages/api-derive/src/session/index.ts b/packages/api-derive/src/session/index.ts new file mode 100644 index 0000000000..47474007b8 --- /dev/null +++ b/packages/api-derive/src/session/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './eraLength'; +export * from './eraProgress'; +export * from './sessionProgress'; diff --git a/packages/api-derive/src/session/sessionProgress.ts b/packages/api-derive/src/session/sessionProgress.ts new file mode 100644 index 0000000000..f8448df5d3 --- /dev/null +++ b/packages/api-derive/src/session/sessionProgress.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { combineLatest, Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber, Option } from '@plugnet/types'; + +import { bestNumber } from '../chain'; +import { drr } from '../util/drr'; + +export function sessionProgress (api: ApiInterface$Rx) { + return (): Observable => + combineLatest( + bestNumber(api)(), + api.query.session.sessionLength(), + api.query.session.lastLengthChange() + ).pipe( + map( + ([bestNumber, sessionLength, lastLengthChange]) => + (bestNumber || new BN(0)) + .sub((lastLengthChange as Option).unwrapOr(new BN(0))) + .add(sessionLength as BlockNumber || new BN(1)) + .mod(sessionLength as BlockNumber || new BN(1)) + ), + drr() + ); +} diff --git a/packages/api-derive/src/staking/controllers.ts b/packages/api-derive/src/staking/controllers.ts new file mode 100644 index 0000000000..f7449d8340 --- /dev/null +++ b/packages/api-derive/src/staking/controllers.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable, combineLatest, of } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Option } from '@plugnet/types'; + +import { drr } from '../util/drr'; + +function allBonds (api: ApiInterface$Rx, stashIds: Array) { + return combineLatest( + stashIds.map((id) => + (api.query.staking.bonded(id) as Observable>) + ) + ); +} + +/** + * @description From the list of stash accounts, retrieve the list of controllers + */ +export function controllers (api: ApiInterface$Rx) { + return (): Observable<[Array, Array>]> => + (api.query.staking.validators() as any as Observable<[Array, any]>) + .pipe( + switchMap(([stashIds]) => + combineLatest( + of(stashIds), + allBonds(api, stashIds) + ) + ), + drr() + ); +} diff --git a/packages/api-derive/src/staking/index.ts b/packages/api-derive/src/staking/index.ts new file mode 100644 index 0000000000..591ca27977 --- /dev/null +++ b/packages/api-derive/src/staking/index.ts @@ -0,0 +1,6 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export * from './controllers'; +export * from './intentionsBalances'; diff --git a/packages/api-derive/src/staking/intentionsBalances.ts b/packages/api-derive/src/staking/intentionsBalances.ts new file mode 100644 index 0000000000..542e8cacff --- /dev/null +++ b/packages/api-derive/src/staking/intentionsBalances.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { AccountId, Vector } from '@plugnet/types'; + +import { DerivedBalancesMap } from '../types'; +import { validatingBalances } from '../balances'; +import { drr } from '../util/drr'; + +/** + * Get the balances for all intentions and their nominators + */ +export function intentionsBalances (api: ApiInterface$Rx) { + return (): Observable => + // tslint:disable-next-line + (api.query.staking.intentions() as Observable>) + .pipe( + switchMap(validatingBalances(api)), + drr() + ) as Observable; +} diff --git a/packages/api-derive/src/types.ts b/packages/api-derive/src/types.ts new file mode 100644 index 0000000000..1d3c804fa7 --- /dev/null +++ b/packages/api-derive/src/types.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { AccountId, Balance, Vote } from '@plugnet/types'; + +export type DerivedBalances = { + accountId: AccountId, + freeBalance: Balance, + nominatedBalance: Balance, + reservedBalance: Balance, + votingBalance: Balance, + stakingBalance: Balance, + nominators?: Array +}; + +export type DerivedFees = { + creationFee: BN, + existentialDeposit: BN, + transactionBaseFee: BN, + transactionByteFee: BN, + transferFee: BN +}; + +export type DerivedBalancesMap = { + [index: string]: DerivedBalances +}; + +export type DerivedReferendumVote = { + accountId: AccountId, + balance: Balance, + vote: Vote +}; diff --git a/packages/api-derive/src/util/drr.spec.ts b/packages/api-derive/src/util/drr.spec.ts new file mode 100644 index 0000000000..240c52603e --- /dev/null +++ b/packages/api-derive/src/util/drr.spec.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { of, timer } from 'rxjs'; + +import { drr } from './drr'; + +describe('drr', () => { + it('should not fire twice the same value', (done) => { + let count = 0; + of(1, 1).pipe(drr()).subscribe(() => { ++count; }); + + setTimeout(() => { + expect(count).toBe(1); + done(); + }, 50); + }); + + it('should be a ReplaySubject(1)', (done) => { + const obs = timer(0, 100).pipe(drr()); // Starts at 0, increments every 100ms + obs.subscribe(); // Fire the observable + + // Subscribe another time after some time, i.e. after the observable has fired + setTimeout(() => { + obs.subscribe((value) => { + expect(value > 1).toBe(true); + done(); + }); + }, 500); + }); +}); diff --git a/packages/api-derive/src/util/drr.ts b/packages/api-derive/src/util/drr.ts new file mode 100644 index 0000000000..dbba4b3575 --- /dev/null +++ b/packages/api-derive/src/util/drr.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { distinctUntilChanged, publishReplay, refCount } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +/** + * Shorthand for distinctUntilChanged(), publishReplay(1) and refCount(). + * + * @ignore + */ +export const drr = () => (source$: Observable): Observable => + source$.pipe( + distinctUntilChanged(), + publishReplay(1), + refCount() + ); diff --git a/packages/api-derive/test/e2e/promise.spec.ts b/packages/api-derive/test/e2e/promise.spec.ts new file mode 100644 index 0000000000..1a14481d27 --- /dev/null +++ b/packages/api-derive/test/e2e/promise.spec.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import ApiPromise from '@plugnet/api/promise/Api'; +import { BlockNumber } from '@plugnet/types'; +import { WsProvider } from '@plugnet/rpc-provider'; + +const WS_LOCAL = 'ws://127.0.0.1:9944/'; +// const WS_POC3 = 'wss://poc3-rpc.polkadot.io/'; + +describe.skip('derive e2e', () => { + let api: ApiPromise; + + beforeAll(() => { + jest.setTimeout(30000); + }); + + beforeEach(async (done) => { + api = await ApiPromise.create(new WsProvider(WS_LOCAL)); + done(); + }); + + it('returns correct results', async () => { + // https://github.com/polkadot-js/api/issues/777 + const block1 = await api.derive.chain.bestNumber(); + await new Promise((resolve) => setTimeout(resolve, 15000)); + const block2 = await api.derive.chain.bestNumber(); + + expect((block1 as BlockNumber).eq(block2)).toBe(false); + }); +}); diff --git a/packages/api-derive/test/e2e/rx.spec.ts b/packages/api-derive/test/e2e/rx.spec.ts new file mode 100644 index 0000000000..0c403dc5de --- /dev/null +++ b/packages/api-derive/test/e2e/rx.spec.ts @@ -0,0 +1,76 @@ +// Copyright 2017-2019 @polkadot/api-derive authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import ApiRx from '@plugnet/api/rx/Api'; +import { ApiInterface$Rx } from '@plugnet/api/types'; +import { BlockNumber } from '@plugnet/types'; +import { WsProvider } from '@plugnet/rpc-provider'; + +const WS_LOCAL = 'ws://127.0.0.1:9944/'; +// const WS_POC3 = 'wss://poc3-rpc.polkadot.io/'; + +describe.skip('derive e2e', () => { + let api: ApiInterface$Rx; + + beforeAll(() => { + jest.setTimeout(10000); + }); + + beforeEach(async (done) => { + api = await ApiRx.create(new WsProvider(WS_LOCAL)).toPromise(); + done(); + }); + + it('derive.chain.bestNumber', async (done) => { + api.derive.chain.bestNumber().subscribe((blockNumber) => { + expect(blockNumber instanceof BlockNumber).toBe(true); + expect((blockNumber as BlockNumber).gten(0)).toBe(true); + done(); + }); + }); + + it('derive.session.sessionProgress', async (done) => { + api.derive.session.sessionProgress().subscribe((progress) => { + expect(progress instanceof BN).toBe(true); + done(); + }); + }); + + it('returns the intentions with balances', async (done) => { + api.derive.staking.intentionsBalances().subscribe((balances) => { + expect(Object.keys(balances as object)).not.toHaveLength(0); + done(); + }); + }); + + // these only work on localhost, not the poc-3 URL + // (and it is assuming it sent at least 1 tx) + describe('accounts', () => { + const ID = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; + const IX = 'F7Gh'; + + it('looks up id & index from id', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.idAndIndex(ID).subscribe(([id, ix]) => { + expect(id.toString()).toEqual(ID); + expect(ix.toString()).toEqual(IX); + done(); + }); + }); + + it('looks up id & index from index', async (done) => { + // @ts-ignore silence warning until we have static types here + api.derive.accounts.idAndIndex(IX).subscribe(([id, ix]) => { + expect(id.toString()).toEqual(ID); + expect(ix.toString()).toEqual(IX); + done(); + }); + }); + }); + + afterAll(() => { + jest.setTimeout(5000); + }); +}); diff --git a/packages/api/LICENSE b/packages/api/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/api/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/api/README.md b/packages/api/README.md new file mode 100644 index 0000000000..d8c33d9a39 --- /dev/null +++ b/packages/api/README.md @@ -0,0 +1,98 @@ +# @polkadot/api + +The Polkadot-JS API provides easy-to-use wrappers around JSONRPC calls that flow from an application to a node. It handles all the encoding and decoding or parameters, provides access to RPC functions and allows for the query of chain state and the submission of transactions. + +The API wrappers provide a standard interface for use - + +- A static `.create()` that returns an API instance when connected, decorated and ready-to use. ApiOptions can include an optional WsProvider and optional custom type definitions `{ provider: , types: }`. +- The above is just a wrapper for `new Api() `, exposing the `isReady` getter +- `api.rpc.
.` provides access to actual RPC calls, be it for queries, submission or retrieving chain information + - [RPC (node interface)](../METHODS_RPC.md) +- `api.query.
.` provides access to chain state queries. These are dynamically populated based on what the runtime provides + - [Storage chain state (runtime node interface)](../METHODS_STORAGE.md) +- `api.tx.
.` provides the ability to create a transaction, like chain state, this list is populated from a runtime query + - [Extrinsics (runtime node interface)](../METHODS_EXTRINSICS.md) + +## API Selection + +There are two flavours of the API provided, one allowing a standard interface via JavaScript Promises and the second provides an Observable wrapper using [RxJS](https://github.com/ReactiveX/rxjs). Depending on your use-case and familiarity, you can choose either (or even both) for your application. + +- [[ApiPromise]] All interface calls returns Promises, including the static `.create(...)`. Additionally any subscription method uses `(value) => {}` callbacks, returning the value as the subscription is updated. +- [[ApiRx]] All interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion subscription-based methods return long-running Observables that update with the latest values. + +## Dynamic by default + +Substrate (upon which Polkadot is built) uses on-chain WASM runtimes, allowing for upgradability. Each runtime defining the actual chain extrinsics (submitted transactions and block intrinsics) as well as available entries in the chain state. Due to this, the API endpoints for queries and transactions are dynamically populated from the running chain. + +Due to this dynamic nature, this API departs from traditional APIs which only has fixed endpoints, driving use by what is available by the runtime. As a start, this generic nature has a learning curve, although the provided documentation, examples and linked documentation tries to make that experience as seamless as possible. + +## Installation & import + +Installation - + +``` +npm install --save @polkadot/api +``` + +Subscribing to blocks via Promise-based API - + +```javascript +import { ApiPromise } from '@polkadot/api'; + +// initialise via static create +const api = await ApiPromise.create(); + +// make a call to retrieve the current network head +api.rpc.chain.subscribeNewHead((header) => { + console.log(`Chain is at #${header.blockNumber}`); +}); +``` + +Subscribing to blocks via RxJS-based API - + +```javascript +import { ApiRx } from '@polkadot/api'; + +// initialise via static create +const api = await ApiRx.create().toPromise(); + +// make a call to retrieve the current network head +api.rpc.chain.subscribeNewHead().subscribe((header) => { + console.log(`Chain is at #${header.blockNumber}`); +}); +``` + +## Registering custom types + +Additional types used by runtime modules can be added when a new instance of the API is created. This is necessary if the runtime modules use types which are not available in the base Substrate runtime. + +```javascript +import { ApiPromise } from '@polkadot/api'; + +// initialise via static create and register custom types +const api = await ApiPromise.create({ + types: { + CustomTypesExample: { + "id": "u32", + "data": "Vec", + "deposit": "Balance", + "owner": "AccountId", + "application_expiry": "Moment", + "whitelisted": "bool", + "challenge_id": "u32" + } + } +}); +``` + +## Users + +Some of the users of the API (let us know if you are missing from the list), include - + +- [Polkadot-JS UI](https://github.com/polkadot-js/apps) A user-interface that allows you to make transactions, query the network or participate in actions on the network such as referendums and staking +- [Polkabot](https://gitlab.com/Polkabot) Polkabot is a Matrix chatbot that keeps an eye on the Polkadot network. You can see Polkabot in action in https://matrix.to/#/#polkadot-network-status:matrix.org +- [Polkawallet.io](https://polkawallet.io) and [Polkawallet (Github)](https://github.com/polkawallet-io/polkawallet-RN/) A mobile wallet for the Polkadot network to manage funds and make transactions, available on both Androind and iOS + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 0000000000..9f7aa67b36 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,41 @@ +{ + "name": "@plugnet/api", + "version": "0.76.1", + "description": "Promise and RxJS wrappers around the Polkadot JS RPC", + "main": "index.js", + "keywords": [ + "Polkadot", + "RxJs" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/api#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/api-derive": "^0.76.1", + "@plugnet/extrinsics": "^0.76.1", + "@plugnet/rpc-provider": "^0.76.1", + "@plugnet/rpc-rx": "^0.76.1", + "@plugnet/storage": "^0.76.1", + "@plugnet/types": "^0.76.1", + "@plugnet/util-crypto": "^0.76.1" + } +} diff --git a/packages/api/src/Base.ts b/packages/api/src/Base.ts new file mode 100644 index 0000000000..eb0d5b54bd --- /dev/null +++ b/packages/api/src/Base.ts @@ -0,0 +1,667 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface } from '@plugnet/rpc-provider/types'; +import { Storage } from '@plugnet/storage/types'; +import { Codec, CodecArg, CodecCallback, RegistryTypes } from '@plugnet/types/types'; +import { RxResult } from './rx/types'; +import { + ApiBaseInterface, ApiInterface$Rx, ApiInterface$Events, ApiOptions, ApiType, + DecoratedRpc, DecoratedRpc$Method, DecoratedRpc$Section, + Derive, DeriveSection, HashResult, U64Result, + OnCallDefinition, OnCallFunction, + QueryableModuleStorage, QueryableStorage, QueryableStorageFunction, + SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics, Signer +} from './types'; + +import EventEmitter from 'eventemitter3'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import decorateDerive, { Derive as DeriveInterface } from '@plugnet/api-derive'; +import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata'; +import RpcBase from '@plugnet/rpc-core'; +import RpcRx from '@plugnet/rpc-rx'; +import storageFromMeta from '@plugnet/storage/fromMetadata'; +import { Event, getTypeRegistry, Hash, Metadata, Method, RuntimeVersion, Null } from '@plugnet/types'; +import { Linkage, LinkageResult } from '@plugnet/types/codec/Linkage'; +import { MethodFunction, ModulesWithMethods } from '@plugnet/types/primitive/Method'; +import { StorageFunction } from '@plugnet/types/primitive/StorageKey'; +import { assert, compactStripLength, isFunction, isObject, isUndefined, logger, u8aToHex } from '@plugnet/util'; +import { cryptoWaitReady } from '@plugnet/util-crypto'; + +import createSubmittable, { SubmittableExtrinsic } from './SubmittableExtrinsic'; + +type MetaDecoration = { + callIndex?: Uint8Array, + meta: any, + method: string, + section: string, + toJSON: () => any +}; + +const INIT_ERROR = `Api needs to be initialised before using, listen on 'ready'`; +const KEEPALIVE_INTERVAL = 15000; + +const l = logger('api/decorator'); + +let pkgJson: { name: string, version: string }; + +try { + pkgJson = require('./package.json'); +} catch (error) { + // development environment + pkgJson = { name: '@polkadot/api', version: '-' }; +} + +/** + * Put the `this.onCall` function of ApiRx here, because it is needed by + * `api._rx`. + */ +function rxOnCall ( + method: OnCallFunction, + params: Array = [], + _callback?: CodecCallback +): RxResult { + return method(...params); +} + +export default abstract class ApiBase implements ApiBaseInterface { + private _derive?: Derive; + private _eventemitter: EventEmitter; + private _extrinsics?: SubmittableExtrinsics; + private _genesisHash?: Hash; + private _isReady: boolean = false; + protected readonly _options: ApiOptions; + private _query?: QueryableStorage; + private _rpc: DecoratedRpc; + protected _rpcBase: RpcBase; // FIXME These two could be merged + protected _rpcRx: RpcRx; // FIXME These two could be merged + private _runtimeMetadata?: Metadata; + private _runtimeVersion?: RuntimeVersion; + private _rx: Partial = {}; + private _type: ApiType; + + /** + * @description Create an instance of the class + * + * @param options Options object to create API instance or a Provider instance + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/api/promise'; + * + * const api = new Api().isReady(); + * + * api.rpc.subscribeNewHead((header) => { + * console.log(`new block #${header.blockNumber.toNumber()}`); + * }); + * ``` + */ + constructor (provider: ApiOptions | ProviderInterface = {}, type: ApiType) { + const options = isObject(provider) && isFunction((provider as ProviderInterface).send) + ? { provider } as ApiOptions + : provider as ApiOptions; + const thisProvider = options.source + ? options.source._rpcBase._provider.clone() + : options.provider; + + this._options = options; + this._type = type; + this._eventemitter = new EventEmitter(); + this._rpcBase = new RpcBase(thisProvider); + + assert(this.hasSubscriptions, 'Api can only be used with a provider supporting subscriptions'); + + this._rpcRx = new RpcRx(thisProvider); + this._rpc = this.decorateRpc(this._rpcRx, this.onCall) as any; // FIXME 3.4.1 + this._rx.rpc = this.decorateRpc(this._rpcRx, rxOnCall); + this._rx.signer = options.signer; + + // we only re-register the types (global) if this is not a cloned instance + if (!options.source) { + this.registerTypes(options.types); + } + + this.init(); + } + + /** + * @description Contains the genesis Hash of the attached chain. Apart from being useful to determine the actual chain, it can also be used to sign immortal transactions. + */ + get genesisHash (): Hash { + assert(!isUndefined(this._genesisHash), INIT_ERROR); + + return this._genesisHash as Hash; + } + + /** + * @description `true` when subscriptions are supported + */ + get hasSubscriptions (): boolean { + return this._rpcBase._provider.hasSubscriptions; + } + + /** + * @description The library information name & version (from package.json) + */ + get libraryInfo (): string { + return `${pkgJson.name} v${pkgJson.version}`; + } + + /** + * @description Yields the current attached runtime metadata. Generally this is only used to construct extrinsics & storage, but is useful for current runtime inspection. + */ + get runtimeMetadata (): Metadata { + assert(!isUndefined(this._runtimeMetadata), INIT_ERROR); + + return this._runtimeMetadata as Metadata; + } + + /** + * @description Contains the version information for the current runtime. + */ + get runtimeVersion (): RuntimeVersion { + assert(!isUndefined(this._runtimeVersion), INIT_ERROR); + + return this._runtimeVersion as RuntimeVersion; + } + + /** + * @description The type of this API instance, either 'rxjs' or 'promise' + */ + get type (): ApiType { + return this._type; + } + + /** + * @description Set an external signer which will be used to sign extrinsic when account passed in is not KeyringPair + */ + setSigner (signer: Signer) { + this._rx.signer = signer; + } + + /** + * @description Derived results that are injected into the API, allowing for combinations of various query results. + * + * @example + *
+ * + * ```javascript + * api.derive.chain.bestNumber((number) => { + * console.log('best number', number); + * }); + * ``` + */ + get derive (): Derive { + assert(!isUndefined(this._derive), INIT_ERROR); + + return this._derive as Derive; + } + + /** + * @description Contains all the chain state modules and their subsequent methods in the API. These are attached dynamically from the runtime metadata. + * + * All calls inside the namespace, is denoted by `section`.`method` and may take an optional query parameter. As an example, `api.query.timestamp.now()` (current block timestamp) does not take parameters, while `api.query.system.accountNonce()` (retrieving the associated nonce for an account), takes the `AccountId` as a parameter. + * + * @example + *
+ * + * ```javascript + * api.query.balances.freeBalance(, (balance) => { + * console.log('new balance', balance); + * }); + * ``` + */ + get query (): QueryableStorage { + assert(!isUndefined(this._query), INIT_ERROR); + + return this._query as QueryableStorage; + } + + /** + * @description Contains all the raw rpc sections and their subsequent methods in the API as defined by the jsonrpc interface definitions. Unlike the dynamic `api.query` and `api.tx` sections, these methods are fixed (although extensible with node upgrades) and not determined by the runtime. + * + * RPC endpoints available here allow for the query of chain, node and system information, in addition to providing interfaces for the raw queries of state (usine known keys) and the submission of transactions. + * + * @example + *
+ * + * ```javascript + * api.rpc.chain.subscribeNewHead((header) => { + * console.log('new header', header); + * }); + * ``` + */ + get rpc (): DecoratedRpc { + return this._rpc; + } + + /** + * @description Contains all the extrinsic modules and their subsequent methods in the API. It allows for the construction of transactions and the submission thereof. These are attached dynamically from the runtime metadata. + * + * @example + *
+ * + * ```javascript + * api.tx.balances + * .transfer(, ) + * .signAndSend(, ({status}) => { + * console.log('tx status', status.asFinalized.toHex()); + * }); + * ``` + */ + get tx (): SubmittableExtrinsics { + assert(!isUndefined(this._extrinsics), INIT_ERROR); + + return this._extrinsics as SubmittableExtrinsics; + } + + /** + * @description Disconnect from the underlying provider, halting all comms + */ + disconnect (): void { + this._rpcBase.disconnect(); + } + + /** + * @description Attach an eventemitter handler to listen to a specific event + * + * @param type The type of event to listen to. Available events are `connected`, `disconnected`, `ready` and `error` + * @param handler The callback to be called when the event fires. Depending on the event type, it could fire with additional arguments. + * + * @example + *
+ * + * ```javascript + * api.on('connected', () => { + * console.log('API has been connected to the endpoint'); + * }); + * + * api.on('disconnected', () => { + * console.log('API has been disconnected from the endpoint'); + * }); + * ``` + */ + on (type: ApiInterface$Events, handler: (...args: Array) => any): this { + this._eventemitter.on(type, handler); + + return this; + } + + /** + * @description Attach an one-time eventemitter handler to listen to a specific event + * + * @param type The type of event to listen to. Available events are `connected`, `disconnected`, `ready` and `error` + * @param handler The callback to be called when the event fires. Depending on the event type, it could fire with additional arguments. + * + * @example + *
+ * + * ```javascript + * api.once('connected', () => { + * console.log('API has been connected to the endpoint'); + * }); + * + * api.once('disconnected', () => { + * console.log('API has been disconnected from the endpoint'); + * }); + * ``` + */ + once (type: ApiInterface$Events, handler: (...args: Array) => any): this { + this._eventemitter.once(type, handler); + + return this; + } + + /** + * @description Register additional user-defined of chain-specific types in the type registry + */ + registerTypes (types?: RegistryTypes): void { + if (types) { + getTypeRegistry().register(types); + } + } + + protected abstract onCall (method: OnCallFunction, params?: Array, callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult; + + private emit (type: ApiInterface$Events, ...args: Array): void { + this._eventemitter.emit(type, ...args); + } + + private init (): void { + let healthTimer: NodeJS.Timeout | null = null; + + this._rpcBase._provider.on('disconnected', () => { + this.emit('disconnected'); + + if (healthTimer) { + clearInterval(healthTimer); + healthTimer = null; + } + }); + + this._rpcBase._provider.on('error', (error) => { + this.emit('error', error); + }); + + this._rpcBase._provider.on('connected', async () => { + this.emit('connected'); + + try { + const [hasMeta, cryptoReady] = await Promise.all([ + this.loadMeta(), + cryptoWaitReady() + ]); + + if (hasMeta && !this._isReady && cryptoReady) { + this._isReady = true; + + this.emit('ready', this); + } + + healthTimer = setInterval(() => { + this._rpcRx.system.health().toPromise().catch(() => { + // ignore + }); + }, KEEPALIVE_INTERVAL); + } catch (error) { + l.error('FATAL: Unable to initialize the API: ', error.message); + } + }); + } + + private async loadMeta (): Promise { + // only load from on-chain if we are not a clone (default path), alternatively + // just use the values from the source instance provided + if (!this._options.source || !this._options.source._isReady) { + this._runtimeMetadata = await this._rpcBase.state.getMetadata(); + this._runtimeVersion = await this._rpcBase.chain.getRuntimeVersion(); + this._genesisHash = await this._rpcBase.chain.getBlockHash(0); + + // get unique types & validate + this.runtimeMetadata.getUniqTypes(false); + } else { + this._runtimeMetadata = this._options.source.runtimeMetadata; + this._runtimeVersion = this._options.source.runtimeVersion; + this._genesisHash = this._options.source.genesisHash; + } + + const extrinsics = extrinsicsFromMeta(this.runtimeMetadata.asV0); + const storage = storageFromMeta(this.runtimeMetadata.asV0); + + this._extrinsics = this.decorateExtrinsics(extrinsics, this.onCall); + this._query = this.decorateStorage(storage, this.onCall) as any; // FIXME 3.4.1 + this._derive = this.decorateDerive(this._rx as ApiInterface$Rx, this.onCall) as any; // FIXME 3.4.1 + + this._rx.genesisHash = this._genesisHash; + this._rx.runtimeVersion = this._runtimeVersion; + this._rx.tx = this.decorateExtrinsics(extrinsics, rxOnCall); + this._rx.query = this.decorateStorage(storage, rxOnCall); + this._rx.derive = this.decorateDerive(this._rx as ApiInterface$Rx, rxOnCall); + + // only inject if we are not a clone (global init) + if (!this._options.source) { + Event.injectMetadata(this.runtimeMetadata.asV0); + Method.injectMethods(extrinsics); + } + + return true; + } + + private decorateFunctionMeta (input: MetaDecoration, output: MetaDecoration): MetaDecoration { + output.meta = input.meta; + output.method = input.method; + output.section = input.section; + output.toJSON = input.toJSON; + + if (input.callIndex) { + output.callIndex = input.callIndex; + } + + return output; + } + + private decorateRpc (rpc: RpcRx, onCall: OnCallDefinition): DecoratedRpc { + return ['author', 'chain', 'state', 'system'].reduce((result, _sectionName) => { + const sectionName = _sectionName as keyof DecoratedRpc; + + result[sectionName] = Object.keys(rpc[sectionName]).reduce((section, methodName) => { + // FIXME Find a better way to know if a particular method is a subscription or not + const needsCallback = methodName.includes('subscribe'); + // These signatures are allowed and exposed here (bit or a stoopid way, but checked + // RPCs and we have 3 max args, with subs max one arg... YMMV) - + // (arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult; + // (arg1: CodecArg, callback: CodecCallback): SubscriptionResult; + // (callback: CodecCallback): SubscriptionResult; + const method = ((...args: Array): C | S => { + let callback: CodecCallback | undefined; + let params = args; + + if (args.length && isFunction(args[args.length - 1])) { + callback = args[args.length - 1]; + params = args.slice(0, args.length - 1); + } + + return onCall(rpc[sectionName][methodName], params, callback, needsCallback); + }) as DecoratedRpc$Method; + + section[methodName] = method; + + return section; + }, {} as DecoratedRpc$Section); + + return result; + }, {} as DecoratedRpc); + } + + private decorateExtrinsics (extrinsics: ModulesWithMethods, onCall: OnCallDefinition): SubmittableExtrinsics { + return Object.keys(extrinsics).reduce((result, sectionName) => { + const section = extrinsics[sectionName]; + + result[sectionName] = Object.keys(section).reduce((result, methodName) => { + result[methodName] = this.decorateExtrinsicEntry(section[methodName], onCall); + + return result; + }, {} as SubmittableModuleExtrinsics); + + return result; + }, {} as SubmittableExtrinsics); + } + + private decorateExtrinsicEntry (method: MethodFunction, onCall: OnCallDefinition): SubmittableExtrinsicFunction { + const decorated: any = (...params: Array): SubmittableExtrinsic => + createSubmittable(this.type, this._rx as ApiInterface$Rx, onCall, method(...params)); + + return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction; + } + + private decorateStorage (storage: Storage, onCall: OnCallDefinition): QueryableStorage { + return Object.keys(storage).reduce((result, sectionName) => { + const section = storage[sectionName]; + + result[sectionName] = Object.keys(section).reduce((result, methodName) => { + result[methodName] = this.decorateStorageEntry(section[methodName], onCall); + + return result; + }, {} as QueryableModuleStorage); + + return result; + }, {} as QueryableStorage); + } + + private decorateStorageEntry (method: StorageFunction, onCall: OnCallDefinition): QueryableStorageFunction { + // These signatures are allowed and exposed here - + // (arg?: CodecArg): CodecResult; + // (arg: CodecArg, callback: CodecCallback): SubscriptionResult; + // (callback: CodecCallback): SubscriptionResult; + const decorated = ((...args: Array): C | S => { + let callback: CodecCallback | undefined; + let params = args; + + if (args.length && isFunction(args[args.length - 1])) { + callback = args[args.length - 1]; + params = args.slice(0, args.length - 1); + } + + if (method.headKey && params.length === 0) { + return this.decorateStorageEntryLinked(method, onCall, callback); + } + + return onCall( + (arg: CodecArg) => this._rpcRx.state + .subscribeStorage([[method, arg]]) + .pipe( + // state_storage returns an array of values, since we have just subscribed to + // a single entry, we pull that from the array and return it as-is + map((result: Array): Codec => + result[0] + ) + ), + params, + callback + ); + }) as QueryableStorageFunction; + + decorated.at = (hash: Hash | Uint8Array | string, arg?: CodecArg): C => + onCall( + (arg: CodecArg) => this._rpcRx.state.getStorage([method, arg], hash), + [arg] + ) as C; + + // FIXME The unknown cast is needed since the onCall result, `C | S` cannot + // be converted from C to the actual result required + decorated.hash = (arg?: CodecArg): HashResult => + onCall( + (arg: CodecArg) => this._rpcRx.state.getStorageHash([method, arg]), + [arg] + ) as unknown as HashResult; + + // FIXME as above... + decorated.size = (arg?: CodecArg): U64Result => + onCall( + (arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]), + [arg] + ) as unknown as U64Result; + + decorated.key = (arg?: CodecArg): string => + u8aToHex(compactStripLength(method(arg))[1]); + + return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction; + } + + private decorateStorageEntryLinked (method: StorageFunction, onCall: OnCallDefinition, callback: CodecCallback | undefined): C | S { + const result: Map]> = new Map(); + let subject: BehaviorSubject; + let head: Codec | null = null; + + // retrieve a value based on the key, iterating if it has a next entry. Since + // entries can be re-linked in the middle of a list, we subscribe here to make + // sure we catch any updates, no matter the list position + const getNext = (key: Codec): Observable => { + return this._rpcRx.state.subscribeStorage([[method, key]]) + .pipe( + switchMap(([data]: [[Codec, Linkage]]) => { + const linkage = data[1]; + + result.set(key, data); + + // iterate from this key to the children, constructing + // entries for all those found and available + if (linkage.next.isSome) { + return getNext(linkage.next.unwrap()); + } + + const keys = []; + const values = []; + let nextKey = head; + + // loop through the results collected, starting at the head an re-creating + // the list. Our map may have old entries, based on the linking these will + // not be returned in the final result + while (nextKey) { + const entry = result.get(nextKey); + + if (!entry) { + break; + } + + const [item, linkage] = entry; + + keys.push(nextKey); + values.push(item); + + nextKey = linkage.next && linkage.next.unwrapOr(null); + } + + const nextResult = values.length + ? new LinkageResult( + [keys[0].constructor as any, keys], + [values[0].constructor as any, values] + ) + : new LinkageResult( + [Null, []], + [Null, []] + ); + + // we set our result into a subject so we have a single observable for + // which the value changes over time. Initially create, follow-up next + if (subject) { + subject.next(nextResult); + } else { + subject = new BehaviorSubject(nextResult); + } + + return subject; + }) + ); + }; + + // this handles the case where the head changes effectively, i.e. a new entry + // appears at the top of the list, the new getNext gets kicked off + return onCall( + (arg: CodecArg) => this._rpcRx.state + .subscribeStorage([arg]) + .pipe( + switchMap(([key]: Array) => { + head = key; + + return getNext(key); + }) + ), + [method.headKey], + callback + ); + } + + private decorateDerive (apiRx: ApiInterface$Rx, onCall: OnCallDefinition): Derive { + const derive = decorateDerive(apiRx, this._options.derives); + + return Object.keys(derive).reduce((result, _sectionName) => { + const sectionName = _sectionName as keyof DeriveInterface; + + result[sectionName] = Object.keys(derive[sectionName]).reduce((section, methodName) => { + // FIXME The callback extraction we do way too much - de-dupe + const method = (...args: Array): C | S => { + let callback: CodecCallback | undefined; + let params = args; + + if (args.length && isFunction(args[args.length - 1])) { + callback = args[args.length - 1]; + params = args.slice(0, args.length - 1); + } + + return onCall((derive[sectionName] as any)[methodName], params, callback, !!callback); + }; + + section[methodName] = method as any; // CodecCallback or CodecArg form + + return section; + }, {} as DeriveSection); + + return result; + }, {} as Derive); + } +} diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts new file mode 100644 index 0000000000..6b03202f2d --- /dev/null +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -0,0 +1,222 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import { AccountId, Address, ExtrinsicStatus, EventRecord, getTypeRegistry, Hash, Index, Method, SignedBlock, Struct, Vector } from '@plugnet/types'; +import { Codec, CodecCallback, IExtrinsic, SignatureOptions } from '@plugnet/types/types'; +import { ApiInterface$Rx, ApiType, OnCallDefinition, Signer } from './types'; + +import { Observable, of, combineLatest } from 'rxjs'; +import { first, map, mergeMap, switchMap, tap } from 'rxjs/operators'; +import { assert, isBn, isFunction, isNumber, isUndefined } from '@plugnet/util'; + +import filterEvents from './util/filterEvents'; + +type StatusCb = (result: SubmittableResult) => any; + +type SumbitableResultResult = + CodecResult extends Observable + ? Observable + : Promise; + +type SumbitableResultSubscription = + SubscriptionResult extends Observable + ? Observable + : Promise<() => void>; + +export class SubmittableResult extends Struct { + constructor (value?: any) { + super({ + events: Vector.with(EventRecord), + status: ExtrinsicStatus + }, value); + } + + /** + * @description the contained events + */ + get events (): Array { + return this.get('events') as Vector; + } + + /** + * @description the status + */ + get status (): ExtrinsicStatus { + return this.get('status') as ExtrinsicStatus; + } + + /** + * @description Finds an EventRecord for the specified method & section + */ + findRecord (section: string, method: string): EventRecord | undefined { + return this.events.find(({ event }) => + event.section === section && event.method === method + ); + } +} + +export interface SubmittableExtrinsic extends IExtrinsic { + send (): SumbitableResultResult; + + send (statusCb: (result: SubmittableResult) => any): SumbitableResultSubscription; + + sign (account: KeyringPair, _options: Partial): this; + + signAndSend (account: KeyringPair | string | AccountId | Address, options?: Partial>): SumbitableResultResult; + + signAndSend (account: KeyringPair | string | AccountId | Address, statusCb: StatusCb): SumbitableResultSubscription; +} + +export default function createSubmittableExtrinsic (type: ApiType, api: ApiInterface$Rx, onCall: OnCallDefinition, extrinsic: Method, trackingCb?: (result: SubmittableResult) => any): SubmittableExtrinsic { + const _extrinsic = new (getTypeRegistry().getOrThrow('Extrinsic'))(extrinsic) as SubmittableExtrinsic; + const _noStatusCb = type === 'rxjs'; + + function updateSigner (updateId: number, status: Hash | SubmittableResult): void { + if ((updateId !== -1) && api.signer && api.signer.update) { + api.signer.update(updateId, status); + } + } + + function statusObservable (status: ExtrinsicStatus): Observable { + if (!status.isFinalized) { + const result = new SubmittableResult({ status }); + + trackingCb && trackingCb(result); + + return of(result); + } + + const blockHash = status.asFinalized; + + return combineLatest( + api.rpc.chain.getBlock(blockHash) as Observable, + api.query.system.events.at(blockHash) as Observable> + ).pipe( + map(([signedBlock, allEvents]) => { + const result = new SubmittableResult({ + events: filterEvents(_extrinsic.hash, signedBlock, allEvents), + status + }); + + trackingCb && trackingCb(result); + + return result; + }) + ); + } + + function sendObservable (updateId: number = -1): Observable { + return (api.rpc.author + .submitExtrinsic(_extrinsic) as Observable) + .pipe( + tap((hash) => { + updateSigner(updateId, hash); + }) + ); + } + + function subscribeObservable (updateId: number = -1): Observable { + return (api.rpc.author + .submitAndWatchExtrinsic(_extrinsic) as Observable) + .pipe( + switchMap((status) => + statusObservable(status) + ), + tap((status) => { + updateSigner(updateId, status); + }) + ); + } + + function expandOptions (options: Partial): SignatureOptions { + return { + blockHash: api.genesisHash, + version: api.runtimeVersion, + ...options + } as SignatureOptions; + } + + const signOrigin = _extrinsic.sign; + Object.defineProperties( + _extrinsic, + { + send: { + value: function (statusCb?: (result: SubmittableResult) => any): SumbitableResultResult | SumbitableResultSubscription { + const isSubscription = _noStatusCb || !!statusCb; + + return onCall( + () => isSubscription + ? subscribeObservable() + : sendObservable(), + [], + statusCb as CodecCallback, + isSubscription + ) as unknown as SumbitableResultSubscription; + } + }, + sign: { + value: function (account: KeyringPair, _options: Partial): SubmittableExtrinsic { + // HACK here we actually override nonce if it was specified (backwards compat for + // the previous signature - don't let userspace break, but allow then time to upgrade) + const options: Partial = isBn(_options) || isNumber(_options) + ? { nonce: _options as any as number } + : _options; + + signOrigin.apply(_extrinsic, [account, expandOptions(options)]); + + return this; + } + }, + signAndSend: { + value: function (account: KeyringPair | string | AccountId | Address, _options?: Partial> | StatusCb, statusCb?: StatusCb): SumbitableResultResult | SumbitableResultSubscription { + let options: Partial> = {}; + + if (isFunction(_options)) { + statusCb = _options; + } else { + options = _options || {}; + } + + const isSubscription = _noStatusCb || !!statusCb; + const isKeyringPair = isFunction((account as KeyringPair).address) && isFunction((account as KeyringPair).sign); + const address = isKeyringPair ? (account as KeyringPair).address() : account.toString(); + let updateId: number | undefined; + + return onCall( + () => (( + isUndefined(options.nonce) + ? api.query.system.accountNonce(address) as Observable + : of(new Index(options.nonce)) + ).pipe( + first(), + mergeMap(async (nonce) => { + if (isKeyringPair) { + this.sign(account as KeyringPair, { ...options, nonce }); + } else { + assert(api.signer, 'no signer exists'); + + updateId = await (api.signer as Signer).sign(_extrinsic, address, expandOptions({ + ...options, + nonce + })); + } + }), + switchMap(() => { + return isSubscription + ? subscribeObservable(updateId) + : sendObservable(updateId) as any; // ??? + }) + ) as Observable), + [], + statusCb as CodecCallback, + isSubscription + ) as unknown as SumbitableResultSubscription; + } + } + } + ); + + return _extrinsic; +} diff --git a/packages/api/src/checkTypes.manual.ts b/packages/api/src/checkTypes.manual.ts new file mode 100644 index 0000000000..ad9f1c5f5f --- /dev/null +++ b/packages/api/src/checkTypes.manual.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// Simple non-runnable checks to test type definitions in the editor itself + +import { ApiPromise } from '@plugnet/api'; +import testKeyring from '@plugnet/keyring/testingPairs'; +import { IExtrinsic, IMethod } from '@plugnet/types/types'; +import { Header, HeaderExtended } from '@plugnet/types'; + +export default async function test () { + const api = await ApiPromise.create(); + const keyring = testKeyring(); + + const intentions = await api.query.staking.intentions(); + console.log('intentions:', intentions); + + await api.query.staking.intentions((intentions) => { + console.log('intentions:', intentions); + }); + + await api.rpc.chain.subscribeNewHead((header: Header) => { + console.log('current blockNumber:', header.blockNumber); + }); + + await api.derive.chain.subscribeNewHead((header: HeaderExtended) => { + console.log('current author:', header.author); + }); + + const transfer = api.tx.balances.transfer(keyring.bob.address(), 12345); + + console.log('transfer as Method', transfer as IMethod); + console.log('transfer as Extrinsic', transfer as IExtrinsic); + + const hash = await transfer.signAndSend(keyring.alice); + console.log('hash:', hash.toHex()); + + const unsub = await api.tx.balances + .transfer(keyring.bob.address(), 12345) + .signAndSend(keyring.alice, ({ status }) => { + console.log('transfer status:', status.type); + + unsub(); + }); +} diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts new file mode 100644 index 0000000000..67a94c3630 --- /dev/null +++ b/packages/api/src/index.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { WsProvider } from '@plugnet/rpc-provider'; +import { assertSingletonPackage } from '@plugnet/util'; + +assertSingletonPackage('@polkadot/api'); + +export { default as ApiPromise } from './promise'; +export { default as ApiRx } from './rx'; +export { default as SubmittableExtrinsic, SubmittableResult } from './SubmittableExtrinsic'; diff --git a/packages/api/src/promise/Api.ts b/packages/api/src/promise/Api.ts new file mode 100644 index 0000000000..f37ea8a3dd --- /dev/null +++ b/packages/api/src/promise/Api.ts @@ -0,0 +1,241 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface } from '@plugnet/rpc-provider/types'; +import { CodecArg, CodecCallback } from '@plugnet/types/types'; +import { RxResult } from '../rx/types'; +import { ApiOptions, OnCallFunction } from '../types'; +import { ApiPromiseInterface, CodecResult, SubscriptionResult } from './types'; + +import { EMPTY } from 'rxjs'; +import { catchError, first, tap } from 'rxjs/operators'; +import { isFunction, assert } from '@plugnet/util'; + +import ApiBase from '../Base'; +import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator'; + +/** + * # @polkadot/api/promise + * + * ## Overview + * + * @name ApiPromise + * + * @description + * ApiPromise is a standard JavaScript wrapper around the RPC and interfaces on the Polkadot network. As a full Promise-based, all interface calls return Promises, including the static `.create(...)`. Subscription calls utilise `(value) => {}` callbacks to pass through the latest values. + * + * The API is well suited to real-time applications where either the single-shot state is needed or use is to be made of the subscription-based features of Polkadot (and Substrate) clients. + * + * @see [[ApiRx]] + * + * ## Usage + * + * Making rpc calls - + *
+ * + * ```javascript + * import ApiPromise from '@plugnet/api/promise'; + * + * // initialise via static create + * const api = await ApiPromise.create(); + * + * // make a subscription to the network head + * api.rpc.chain.subscribeNewHead((header) => { + * console.log(`Chain is at #${header.blockNumber}`); + * }); + * ``` + *
+ * + * Subscribing to chain state - + *
+ * + * ```javascript + * import { ApiPromise, WsProvider } from '@plugnet/api'; + * + * // initialise a provider with a specific endpoint + * const provider = new WsProvider('wss://example.com:9944') + * + * // initialise via isReady & new with specific provider + * const api = await new ApiPromise(provider).isReady; + * + * // retrieve the block target time + * const blockPeriod = await api.query.timestamp.blockPeriod().toNumber(); + * let last = 0; + * + * // subscribe to the current block timestamp, updates automatically (callback provided) + * api.query.timestamp.now((timestamp) => { + * const elapsed = last + * ? `, ${timestamp.toNumber() - last}s since last` + * : ''; + * + * last = timestamp.toNumber(); + * console.log(`timestamp ${timestamp}${elapsed} (${blockPeriod}s target)`); + * }); + * ``` + *
+ * + * Submitting a transaction - + *
+ * + * ```javascript + * import ApiPromise from '@plugnet/api/promise'; + * + * ApiPromise.create().then((api) => { + * const nonce = await api.query.system.accountNonce(keyring.alice.address()); + * + * api.tx.balances + * // create transfer + * transfer(keyring.bob.address(), 12345) + * // sign the transcation + * .sign(keyring.alice, { nonce }) + * // send the transaction (optional status callback) + * .send((status) => { + * console.log(`current status ${status.type}`); + * }) + * // retrieve the submitted extrinsic hash + * .then((hash) => { + * console.log(`submitted with hash ${hash}`); + * }); + * }); + * ``` + */ +export default class ApiPromise extends ApiBase implements ApiPromiseInterface { + private _isReadyPromise: Promise; + + /** + * @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance. + * + * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a + * provider (see the constructor arguments) + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/api/promise'; + * + * Api.create().then(async (api) => { + * const timestamp = await api.query.timestamp.now(); + * + * console.log(`lastest block timestamp ${timestamp}`); + * }); + * ``` + */ + static create (options: ApiOptions | ProviderInterface = {}): Promise { + return new ApiPromise(options).isReady; + } + + /** + * @description Creates an instance of the ApiPromise class + * + * @param options Options to create an instance. This can be either [[ApiOptions]] or + * an [[WsProvider]]. + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/api/promise'; + * + * new Api().isReady.then((api) => { + * api.rpc.subscribeNewHead((header) => { + * console.log(`new block #${header.blockNumber.toNumber()}`); + * }); + * }); + * ``` + */ + constructor (options?: ApiOptions | ProviderInterface) { + super(options, 'promise'); + + this._isReadyPromise = new Promise((resolveReady) => + super.once('ready', () => + resolveReady(this) + ) + ); + } + + /** + * @description Promise that returns the first time we are connected and loaded + */ + get isReady (): Promise { + return this._isReadyPromise; + } + + /** + * @description Returns a clone of this ApiPromise instance (new underlying provider connection) + */ + clone (): ApiPromise { + return new ApiPromise({ + ...this._options, + source: this + }); + } + + /** + * @description Creates a combinator that can be used to combine the latest results from multiple subscriptions + * @param fns An array of function to combine, each in the form of `(cb: (value: void)) => void` + * @param callback A callback that will return an Array of all the values this combinator has been applied to + * @example + *
+ * + * ```javascript + * const address = '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7'; + * + * // combines values from balance & nonce as it updates + * api.combineLatest([ + * api.rpc.chain.subscribeNewHead, + * [api.query.balances.freeBalance, address], + * (cb) => api.query.system.accountNonce(address, cb) + * ], ([head, balance, nonce]) => { + * console.log(`#${head.number}: You have ${balance} units, with ${nonce} transactions sent`); + * }); + * ``` + */ + async combineLatest (fns: Array]>, callback: CombinatorCallback): SubscriptionResult { + const combinator = new Combinator(fns, callback); + + return (): void => { + combinator.unsubscribe(); + }; + } + + protected onCall (method: OnCallFunction, params: Array = [], callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult { + // When we need a subscription, ensure that a valid callback is actually passed + assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions'); + + if (!callback) { + return method(...params).pipe(first()).toPromise(); + } + + // FIXME TSLint shouts that type assertion is unnecessary, but tsc shouts + // when I remove it... + // tslint:disable-next-line + return new Promise((resolve, reject) => { + let isCompleted = false; + const subscription = method(...params) + .pipe( + // if we find an error (invalid params, etc), reject the promise + catchError((error) => { + if (!isCompleted) { + isCompleted = true; + + reject(error); + } + + // we don't want to continue, so empty observable it is + return EMPTY; + }), + // upon the first result, resolve the with the unsub function + tap(() => { + if (!isCompleted) { + isCompleted = true; + + resolve(() => subscription.unsubscribe()); + } + }) + ) + .subscribe(callback); + }) as SubscriptionResult; + } +} diff --git a/packages/api/src/promise/Combinator.ts b/packages/api/src/promise/Combinator.ts new file mode 100644 index 0000000000..d28b16e169 --- /dev/null +++ b/packages/api/src/promise/Combinator.ts @@ -0,0 +1,86 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { SubscriptionResult } from './types'; + +import { isFunction } from '@plugnet/util'; + +export type CombinatorCallback = (value: Array) => any; +export type CombinatorFunction = { + (cb: (value: any) => any): SubscriptionResult +}; + +export default class Combinator { + protected _allHasFired: boolean = false; + protected _callback: CombinatorCallback; + protected _fired: Array = []; + protected _fns: Array = []; + protected _isActive: boolean = true; + protected _results: Array = []; + protected _subscriptions: Array = []; + + constructor (fns: Array]>, callback: CombinatorCallback) { + this._callback = callback; + this._subscriptions = fns.map(async (input, index) => { + const [fn, ...args] = Array.isArray(input) + ? input + : [input]; + + this._fired.push(false); + this._fns.push(fn); + + // @ts-ignore Not quite 100% how to have a variable number at the front here + return fn(...args, this.createCallback(index)); + }); + } + + protected allHasFired (): boolean { + if (!this._allHasFired) { + this._allHasFired = this._fired.filter((hasFired) => !hasFired).length === 0; + } + + return this._allHasFired; + } + + protected createCallback (index: number) { + return (value: any): void => { + this._fired[index] = true; + this._results[index] = value; + + this.triggerUpdate(); + }; + } + + protected triggerUpdate (): void { + if (!this._isActive || !isFunction(this._callback) || !this.allHasFired()) { + return; + } + + try { + this._callback(this._results); + } catch (error) { + // swallow, we don't want the handler to trip us up + } + } + + unsubscribe (): void { + if (!this._isActive) { + return; + } + + this._isActive = false; + + this._subscriptions.forEach(async (subscription) => { + try { + const unsubscribe = await subscription; + + if (isFunction(unsubscribe)) { + unsubscribe(); + } + } catch (error) { + // ignore + } + }); + } +} diff --git a/packages/api/src/promise/Combinators.spec.ts b/packages/api/src/promise/Combinators.spec.ts new file mode 100644 index 0000000000..9d60b695e5 --- /dev/null +++ b/packages/api/src/promise/Combinators.spec.ts @@ -0,0 +1,95 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { SubscriptionResult } from './types'; + +import Combinator from './Combinator'; + +describe('Combinator', () => { + let fns: Array<(value: any) => void> = []; + const storeFn = async (cb: (value: any) => void): SubscriptionResult => { + fns.push(cb); + + return () => undefined; + }; + + beforeEach(() => { + fns = []; + }); + + it('it triggers on all values', (done) => { + let count = 0; + const combinator = new Combinator( + [storeFn], + (value: Array) => { + expect(value[0]).toEqual(`test${count}`); + + count++; + + if (count === 3) { + done(); + } + } + ); + + fns[0]('test0'); + fns[0]('test1'); + fns[0]('test2'); + + expect(combinator).toBeDefined(); + }); + + it('combines values from 2 sources, firing when it has all results', (done) => { + const combinator = new Combinator( + [storeFn, storeFn], + (value: Array) => { + expect(value).toEqual(['test0', 'test1']); + + done(); + } + ); + + fns[0]('test0'); + fns[1]('test1'); + + expect(combinator).toBeDefined(); + }); + + it('combines values from 2 sources, allowing multiple updates', (done) => { + let count = 0; + const combinator = new Combinator( + [storeFn, storeFn], + (value: Array) => { + expect(value).toEqual( + count === 0 + ? ['test0', 'test1'] + : ['test2', 'test1']); + + count++; + + if (count === 2) { + done(); + } + } + ); + + fns[0]('test0'); + fns[1]('test1'); + fns[0]('test2'); + + expect(combinator).toBeDefined(); + }); + + it('unsubscribes as required', (done) => { + const mocker = async () => done; + const combinator = new Combinator([ + mocker, + async () => () => void 0 + ], (value: Array) => { + // ignore + }); + + combinator.unsubscribe(); + }); +}); diff --git a/packages/api/src/promise/index.ts b/packages/api/src/promise/index.ts new file mode 100644 index 0000000000..e0fb61d6c5 --- /dev/null +++ b/packages/api/src/promise/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as ApiPromise } from './Api'; + +export default ApiPromise; diff --git a/packages/api/src/promise/types.ts b/packages/api/src/promise/types.ts new file mode 100644 index 0000000000..48330f8f80 --- /dev/null +++ b/packages/api/src/promise/types.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec } from '@plugnet/types/types'; +import { QueryableStorageFunction as QueryableStorageFunctionBase, SubmittableExtrinsicFunction as SubmittableExtrinsicFunctionBase } from '../types'; + +import ApiBase from '../Base'; +import { SubmittableExtrinsic as SubmittableExtrinsicBase } from '../SubmittableExtrinsic'; + +export type SubscriptionResult = Promise<() => any>; + +export type CodecResult = Promise; + +export interface ApiPromiseInterface extends ApiBase { + readonly isReady: Promise; +} + +export type QueryableStorageFunction = QueryableStorageFunctionBase; +export type SubmittableExtrinsic = SubmittableExtrinsicBase; +export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase; diff --git a/packages/api/src/rx/Api.ts b/packages/api/src/rx/Api.ts new file mode 100644 index 0000000000..009844357e --- /dev/null +++ b/packages/api/src/rx/Api.ts @@ -0,0 +1,205 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface } from '@plugnet/rpc-provider/types'; +import { CodecArg } from '@plugnet/types/types'; +import { ApiRxInterface, RxResult } from './types'; +import { ApiOptions, OnCallFunction } from '../types'; + +import { Observable, from } from 'rxjs'; + +import ApiBase from '../Base'; + +/** + * # @polkadot/api/rx + * + * ## Overview + * + * @name ApiRx + * + * @description + * ApiRx is a powerfull RxJS Observable wrapper around the RPC and interfaces on the Polkadot network. As a full Observable API, all interface calls return RxJS Observables, including the static `.create(...)`. In the same fashion and subscription-based methods return long-running Observables that update with the latest values. + * + * The API is well suited to real-time applications where the latest state is needed, unlocking the subscription-based features of Polkadot (and Substrate) clients. Some familiarity with RxJS is a requirement to use the API, however just understanding `.subscribe` and `.pipe` on Observables will unlock full-scale use thereof. + * + * @see [[ApiPromise]] + * + * ## Usage + * + * Making rpc calls - + *
+ * + * ```javascript + * import ApiRx from '@plugnet/api/rx'; + * + * // initialise via Promise & static create + * const api = await ApiRx.create().toPromise(); + * + * // make a call to retrieve the current network head + * api.rpc.chain.subscribeNewHead().subscribe((header) => { + * console.log(`Chain is at #${header.blockNumber}`); + * }); + * ``` + *
+ * + * Subscribing to chain state - + *
+ * + * ```javascript + * import { combineLatest } from 'rxjs'; + * import { pairwise, switchMap } from 'rxjs/operators'; + * import { ApiRx, WsProvider } from '@plugnet/api'; + * + * + * // initialise a provider with a specific endpoint + * const provider = new WsProvider('wss://example.com:9944') + * + * // initialise via isReady & new with specific provider + * new ApiRx(provider) + * .isReady + * .pipe( + * switchMap((api) => + * combineLatest([ + * api.query.timestamp.blockPeriod(), + * api.query.timestamp.now().pipe(pairwise()) + * ]) + * ) + * ) + * .subscribe(([blockPeriod, timestamp]) => { + * const elapsed = timestamp[1].toNumber() - timestamp[0].toNumber(); + * console.log(`timestamp ${timestamp[1]} \nelapsed ${elapsed} \n(${blockPeriod}s target)`); + * }); + * ``` + *
+ * + * Submitting a transaction - + *
+ * + * ```javascript + * import { first, switchMap } from 'rxjs/operators'; + * import ApiRx from '@plugnet/api/rx'; + * + * // import the test keyring (already has dev keys for Alice, Bob, Charlie, Eve & Ferdie) + * import testingPairs from '@plugnet/keyring/testingPairs'; + * const keyring = testingPairs(); + * + * // get api via Promise + * const api = await ApiRx.create().toPromise(); + * + * // retrieve nonce for the account + * api.query.system + * .accountNonce(keyring.alice.address()) + * .pipe( + * first(), + * // pipe nonce into transfer + * switchMap((nonce) => + * api.tx.balances + * // create transfer + * .transfer(keyring.bob.address(), 12345) + * // sign the transcation + * .sign(keyring.alice, { nonce }) + * // send the transaction + * .send() + * ) + * ) + * // subscribe to overall result + * .subscribe(({ status }) => { + * if (status.isFinalized) { + * console.log('Completed at block hash', status.asFinalized.toHex()); + * } + * }); + * ``` + */ +export default class ApiRx extends ApiBase implements ApiRxInterface { + private _isReadyRx: Observable; + + /** + * @description Creates an ApiRx instance using the supplied provider. Returns an Observable containing the actual Api instance. + * + * @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or [[WsProvider]] + * + * @example + *
+ * + * ```javascript + * import { switchMap } from 'rxjs/operators'; + * import Api from '@plugnet/api/rx'; + * + * Api.create() + * .pipe( + * switchMap((api) => + * api.rpc.chain.subscribeNewHead() + * )) + * .subscribe((header) => { + * console.log(`new block #${header.blockNumber.toNumber()}`); + * }); + * ``` + */ + static create (options?: ApiOptions | ProviderInterface): Observable { + return new ApiRx(options).isReady; + } + + /** + * @description Create an instance of the ApiRx class + * + * @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]] + * + * @example + *
+ * + * ```javascript + * import { switchMap } from 'rxjs/operators'; + * import Api from '@plugnet/api/rx'; + * + * new Api().isReady + * .pipe( + * switchMap((api) => + * api.rpc.chain.subscribeNewHead() + * )) + * .subscribe((header) => { + * console.log(`new block #${header.blockNumber.toNumber()}`); + * }); + * ``` + */ + constructor (provider?: ApiOptions | ProviderInterface) { + super(provider, 'rxjs'); + + this._isReadyRx = from( + // convinced you can observable from an event, however my mind groks this form better + new Promise((resolveReady) => + super.on('ready', () => + resolveReady(this) + ) + ) + ) as Observable; + } + + /** + * @description Observable that carries the connected state for the provider. Results in a boolean flag that is true/false based on the connectivity. + */ + get isConnected (): Observable { + return this._rpcRx.isConnected(); + } + + /** + * @description Observable that returns the first time we are connected and loaded + */ + get isReady (): Observable { + return this._isReadyRx; + } + + /** + * @description Returns a clone of this ApiRx instance (new underlying provider connection) + */ + clone (): ApiRx { + return new ApiRx({ + ...this._options, + source: this + }); + } + + protected onCall (method: OnCallFunction, params: Array = []): RxResult { + return method(...params); + } +} diff --git a/packages/api/src/rx/index.ts b/packages/api/src/rx/index.ts new file mode 100644 index 0000000000..936c2e61ce --- /dev/null +++ b/packages/api/src/rx/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as ApiRx } from './Api'; + +export default ApiRx; diff --git a/packages/api/src/rx/types.ts b/packages/api/src/rx/types.ts new file mode 100644 index 0000000000..92ffb0896f --- /dev/null +++ b/packages/api/src/rx/types.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { Codec } from '@plugnet/types/types'; + +import ApiBase from '../Base'; + +export type RxResult = Observable; + +export interface ApiRxInterface extends ApiBase { + readonly isConnected: Observable; + readonly isReady: Observable; +} diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts new file mode 100644 index 0000000000..08336761c0 --- /dev/null +++ b/packages/api/src/types.ts @@ -0,0 +1,172 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { DeriveCustom } from '@plugnet/api-derive'; +import { ProviderInterface } from '@plugnet/rpc-provider/types'; +import { RpcRxInterface$Events } from '@plugnet/rpc-rx/types'; +import { Hash, Metadata, RuntimeVersion, u64 as U64 } from '@plugnet/types'; +import { CodecArg, CodecCallback, IExtrinsic, RegistryTypes, SignatureOptions } from '@plugnet/types/types'; +import { MethodFunction } from '@plugnet/types/primitive/Method'; +import { StorageFunction } from '@plugnet/types/primitive/StorageKey'; + +import { RxResult } from './rx/types'; +import { SubmittableResult, SubmittableExtrinsic } from './SubmittableExtrinsic'; +import ApiBase from './Base'; + +export type OnCallDefinition = (method: OnCallFunction, params?: Array, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult; + +export type OnCallFunction = (...params: Array) => CodecResult | SubscriptionResult; + +// checked against max. params in jsonrpc, 1 for subs, 3 without +export interface DecoratedRpc$Method { + (callback: CodecCallback): SubscriptionResult; + (arg1: CodecArg, callback: CodecCallback): SubscriptionResult; + (arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult; +} + +export interface DecoratedRpc$Section { + [index: string]: DecoratedRpc$Method; +} + +export interface DecoratedRpc { + author: DecoratedRpc$Section; + chain: DecoratedRpc$Section; + state: DecoratedRpc$Section; + system: DecoratedRpc$Section; +} + +export type HashResult = + CodecResult extends Observable + ? Observable + : Promise; + +export type U64Result = + CodecResult extends Observable + ? Observable + : Promise; + +export interface QueryableStorageFunctionBase extends StorageFunction { + (arg?: CodecArg): CodecResult; + at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult; + hash: (arg?: CodecArg) => HashResult; + key: (arg?: CodecArg) => string; + size: (arg?: CodecArg) => U64Result; +} + +interface QueryableStorageFunctionPromise extends QueryableStorageFunctionBase { + (callback: CodecCallback): SubscriptionResult; + (arg: CodecArg, callback: CodecCallback): SubscriptionResult; +} + +export type QueryableStorageFunction = + CodecResult extends Observable + ? QueryableStorageFunctionBase + : QueryableStorageFunctionPromise; + +export interface QueryableModuleStorage { + [index: string]: QueryableStorageFunction; +} + +export interface QueryableStorage { + [index: string]: QueryableModuleStorage; +} + +export interface SubmittableExtrinsicFunction extends MethodFunction { + (...params: Array): SubmittableExtrinsic; +} + +export interface SubmittableModuleExtrinsics { + [index: string]: SubmittableExtrinsicFunction; +} + +export interface SubmittableExtrinsics { + [index: string]: SubmittableModuleExtrinsics; +} + +export interface DeriveMethodBase { + (...params: Array): CodecResult; +} + +interface DeriveMethodPromise extends DeriveMethodBase { + (callback: CodecCallback): SubscriptionResult; + (arg0: CodecArg, callback: CodecCallback): SubscriptionResult; + (arg0: CodecArg, arg1: CodecArg, callback: CodecCallback): SubscriptionResult; + (arg0: CodecArg, arg1: CodecArg, arg2: CodecArg, callback: CodecCallback): SubscriptionResult; +} + +export type DeriveMethod = + CodecResult extends Observable + ? DeriveMethodBase + : DeriveMethodPromise; + +export interface DeriveSection { + [index: string]: DeriveMethod; +} + +export interface Derive { + [index: string]: DeriveSection; +} + +export interface ApiOptions { + /** + * @description Add custom derives to be injected + */ + derives?: DeriveCustom; + /** + * @description Transport Provider from rpc-provider. If not specified, it will default to + * connecting to a WsProvider connecting localhost with the default port, i.e. `ws://127.0.0.1:9944` + */ + provider?: ProviderInterface; + /** + * @description An external signer which will be used to sign extrinsic when account passed in is not KeyringPair + */ + signer?: Signer; + /** + * @description The source object to use for runtime information (only used when cloning) + */ + source?: ApiBase; + /** + * @description Additional types used by runtime modules. This is nessusary if the runtime modules + * uses types not available in the base Substrate runtime. + */ + types?: RegistryTypes; +} + +export interface ApiInterface$Decorated { + genesisHash: Hash; + hasSubscriptions: boolean; + runtimeMetadata: Metadata; + runtimeVersion: RuntimeVersion; + derive: Derive; + query: QueryableStorage; + rpc: DecoratedRpc; + tx: SubmittableExtrinsics; + signer?: Signer; +} + +export type ApiInterface$Rx = ApiInterface$Decorated; + +export type ApiInterface$Events = RpcRxInterface$Events | 'ready'; + +export type ApiType = 'promise' | 'rxjs'; + +export interface ApiBaseInterface extends Readonly> { + readonly type: ApiType; + + on: (type: ApiInterface$Events, handler: (...args: Array) => any) => this; + once: (type: ApiInterface$Events, handler: (...args: Array) => any) => this; +} + +export interface Signer { + /** + * @description Signs an extrinsic, returning an id (>0) that can be used to retrieve updates + */ + sign (extrinsic: IExtrinsic, address: string, options: SignatureOptions): Promise; + + /** + * @description Receives an update for the extrinsic signed by a `signer.sign` + */ + update?: (id: number, status: Hash | SubmittableResult) => void; +} diff --git a/packages/api/src/util/filterEvents.ts b/packages/api/src/util/filterEvents.ts new file mode 100644 index 0000000000..a6fbf63766 --- /dev/null +++ b/packages/api/src/util/filterEvents.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { EventRecord, SignedBlock, U8a } from '@plugnet/types'; + +import l from './logging'; + +export default function filterEvents (extHash: U8a, { block: { extrinsics, header } }: SignedBlock, allEvents: Array): Array | undefined { + // extrinsics to hashes + const myHash = extHash.toHex(); + const allHashes = extrinsics.map((ext) => ext.hash.toHex()); + + // find the index of our extrinsic in the block + const index = allHashes.indexOf(myHash); + + // if we do get the block after finalized, it _should_ be there + if (index === -1) { + l.warn(`block ${header.hash}: Unable to find extrinsic ${myHash} inside ${allHashes}`); + return; + } + + return allEvents.filter(({ phase }) => + // only ApplyExtrinsic has the extrinsic index + phase.isApplyExtrinsic && phase.asApplyExtrinsic.eqn(index) + ); +} diff --git a/packages/api/src/util/logging.ts b/packages/api/src/util/logging.ts new file mode 100644 index 0000000000..3f87def97e --- /dev/null +++ b/packages/api/src/util/logging.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { logger } from '@plugnet/util'; + +const l = logger('api/util'); + +export default l; diff --git a/packages/api/test/data/erc20.json b/packages/api/test/data/erc20.json new file mode 100644 index 0000000000..f3cca08069 --- /dev/null +++ b/packages/api/test/data/erc20.json @@ -0,0 +1,100 @@ +{ + "name": "Erc20", + "deploy": { + "args": [ + { + "name": "init_value", + "type": "Balance" + } + ] + }, + "messages": [ + { + "name": "total_supply", + "selector": 949181842, + "mutates": false, + "args": [], + "return_type": "Balance" + }, + { + "name": "balance_of", + "selector": 3827153702, + "mutates": false, + "args": [ + { + "name": "owner", + "type": "Address" + } + ], + "return_type": "Balance" + }, + { + "name": "allowance", + "selector": 3057219184, + "mutates": false, + "args": [ + { + "name": "owner", + "type": "Address" + }, + { + "name": "spender", + "type": "Address" + } + ], + "return_type": "Balance" + }, + { + "name": "transfer", + "selector": 3374842663, + "mutates": true, + "args": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "value", + "type": "Balance" + } + ], + "return_type": "bool" + }, + { + "name": "approve", + "selector": 431186122, + "mutates": true, + "args": [ + { + "name": "spender", + "type": "Address" + }, + { + "name": "value", + "type": "Balance" + } + ], + "return_type": "bool" + }, + { + "name": "transfer_from", + "selector": 197482595, + "mutates": true, + "args": [ + { + "name": "from", + "type": "Address" + }, + { + "name": "to", + "type": "Address" + }, + { + "name": "value", + "type": "Balance" + } + ], + "return_type": "bool" + } + ] +} diff --git a/packages/api/test/data/erc20.wasm b/packages/api/test/data/erc20.wasm new file mode 100644 index 0000000000000000000000000000000000000000..dcf4ee018b10887593a2af0b74a04249e1e567c4 GIT binary patch literal 31213 zcmdsg4Qw3Oedl{`W_M?IcEyn`+0=*a|BRG)8A~E#IV4Rx3Ew)h?7EIWn)IABsYH=$ zi$jW(mXvL!j<}Asj+o?7wGCPw(4y=$s9jtQsJ-;+LjzPexUGQ80V*!V>3|%d0&bzU zzJrPeJ>_b|hu{DIW_C$Qu~IvS;A}uU^JeCK{y%>IkN2o177l5pl>UWV_M4^SYQI@p z(#QGVrTrS0O6^yAX}`C$w50a?OZ=mk^b+1tT+DCs<#G3hUfN&4n_PO_l^$37H5xd+ zBo|pFddd6;UG|!D&sLhxFYcS1n4N95l#hZ41q+j{iN(o-`xd6Z&{T!1iYMm}A5%pX zufyBZbBB*E_PpCu*nP8)>uoOXTUeZLO+3?7C4A4N&-9cADCJJInu|wUbBcReJk&fi z-#TWriM$i~OSwdQ z8~uVltb?F{HL_T5BKdm*qk`L@4&=D)6O&>o4j1w6|o&N!N`^41WB_YKn~2Y2uT4 zxTZ*xKWvoMu@{meGi-cPmjdR!Q%yW;7Sp6)t0@(1kR}E42X&FW8EY1!lX^21NDmt& z-)Q;9w-?lGnt1kC=n+n+}3-N3b7DWr*!nJZwn@sgne_@D0~6fk>*e9jFsKYtQ?Xsm9qDpse% zW-IyR@l`)5>!-2t#>tm91M=%eK_WobDBSt7!srCbGC=d0&w8N{y1bg zc{7O@-Hp-lBoy$1O5>u{u{Pr#KG9T!#?a(J6=_jL`r(=(W0z8^#*`vI)=VJYo|*}J z$~6_z*i=nO9@8FIf>Ebv4F6bbAG|g+lX%+| zd6u9ZwVNFeCH;_2m;lTdmqck&(dNxix&vMyE6|^kKj8H&OnwujRCG6<9RlyX2;RY37%1cTVL!X1 zy`rA?)-)+gq<1P6-PMO_Akvni9kCBxO=M;g&(b3`L!ONvt9h2ln<2Be#!vtPLz9Vi zfaEY2nrrbv2=f1+)|fsaSe}yxPm)k8>-eptqT3t=h5rcxcbl>_lbG#5BSA_kszY*A zL975ADDG$B4kCuUV@l+!2!yv&`PMw>kJ~63PVf@uPmLH*p&9TZTtP-9-cF^g!DN6I zZ4^}ibXc)?Ol?&i@}orceAulogoMMFkbvkGVcH)N@!T_J9)iXY6l12ua2I72{M70( zrO-0>Lh2HZ3a5JOGUJh#7|4ZQmgf}^Ubl)&WYi1(hY-l`u_huvnBGa{Pc(O?zo>u#buA@0CQ z0Qy3PYAqQ;zOE)ikO)usy--YIDp>Up8T-yV@4Qobu(b!>Vx;eAO)$K+J_;ZQwkyYw z8Ji^-kA^+SB9%XPTW1oZit#(u8XR5TZi5Q>ipz7-PuWONyIUds|@0 zZu?x!?L&z^CK@ryM4cK_eUJ;xAAPCS(XK@MOF%k%YZ}%%n(r%E6^Piv^3qig zvQkGI6H_z1ZqAj#0-P~L82*ki{*rbG9<#5ZF}DE!a@(+xRCFIaB=+m4fh-^gVHbI} znnoW>6k0MN=~&^%kM56^d<*n!$7dk>9|O31t>L|#6dQE%6;LY`UjbE;dU#6$4U2(4 zTr(hCGH@?NV@Y9LCB`m2SS#S`DEr#uZ}-#+ROl)7QL3VkqE0fYH7x-^3i6)`rYdV~ zR*xW7)j|#qRfthCVX@M5#G}IVVs`H#y+<@mbl+w-Au-_1^J$`48` z>LWa%U_yi&i5rwrF|~SY{Hj*+3@Rl!T=4KaOf zkculcNPMf0z@qZV7Xmkoy?iD|e;oFFMvGh6r3y*6nyMY9%&1x%Hy+r;b4pG{_s2ds zhkQ%|2p;tp$e&5dRCuITq@Y&fg-d*hLpYVjXOar}57)|6p%RskB>tl{k31?K*;DgF zVSJT<itf<^j6PMwychQY(Lwz@JUNHAkSs;Su|y<6ve8o-KyUT#w8McLpAC zH>GF?rf!f5on)5oF{e~iqS0%!hcv_ndd(t+dkw{DUpOLt2M!ih%Hw1mo_Cf_L4H#- zNZqIcnb3ONA%~v%CL~t1Zv9};2kA_rNIwc~f=>Rx=pu~jw#hIAIWruFv>4gIc5E7emVD^c1yyK&@Y zEdsbLZ^Cej8f5JEbea_FMlq!FED6srf4~J4q;Nk(51?&7E(u#fI~GJ)*HiMTM5rP9 zL{_WE_esG~F{Ex`M*8J}8OV=4b||42{JoAokvYLA&D|>z47~pCJsW&ceh*WST{OnF zjq`bQ#E(>gBU_i$Xp&qMJ#{7@X==XD*I@q0?gefT{XNB9|! zL;Uo|>+y3&$2Z~kq>gXK@BVlLen#Vs_=%t=2fSBxd<%ZtI=&UZPsRQC8HzXICx|!W zXGO;q{5~IV!O!FIR{YfC+wh~}+wIxov3B_OHoCn-TRXI+LzNELnl^T5Lx*ne&`q7B z(9p}B#L%WrypyUF()tcX9qQ{)xsw#hQJ?l+|kg> zROloH3OkLFb%s6s}$266!IgVwsaZ; z%gG(&QLWRsb2-^2FWo|h`a6xvaxKhEC(I zd02dGYWN|hyufBw4Ai37TW z5+Zq0raS2a{N#h3xQ{+W6~4Kv6GudJ2j6V##5d6ns?c5X%?7H|2Wh+9+)CRh=GT%= zT%jE_z&9W2#9Ntl72OU`l(H*2rHWi$Q7LVa%kwIwt#VmU={C7~Ri|`|T()&ex60)O zl~TW44yCk7u7Z>{%heg3(oJ%CQm1sYTwYWuZIH{+ls3v$1XnqC>UEt`SuPQo>XXY$ zDy2v+$7d*%tNxVM%hg$(QdKTb>y%1zi3n0nF3+iy*2yKM6pVN(+$q?p7bUV!h)nbl zFexMy>p$3N8G!43n{B=5L57oXlmlKuOJebrJ*T6GQ09|{9n8-GTM^_5FLhS?=Ci0#UkXGK1@MFdd7L_$jiKRD!hf; z!NQ7J`(?cuReU<9sjfesbo7X2KnoQd!0!O&+mYh78vQiPRQRHSL|BJ3^b=cRxFN+m zpu_v~S~DL6hHq8$C?+|;cyLj5{j_GUfG#H7-}Q7E6n4hJ88pL-$cQQ_$DTKiuT_H3 zF(_wH7n6!Li*d;=!8w#gMZ9q;0SO{-8GBaQC94H41f(C?LYjgHwLu7=ly z26sH-Z4Lc|&v8~=EnqzhHcF!>V6l~`V29G^iCQ5f#Kq$xSkam)CeIr3wp2}MVPn8O zRqVQ_iei|Y_Z)QGl{1L29j$VR?}0DhQUuG0S>oZ`ImRYc)kDC~dj!S@JV#`hzu+rUw5<2^q5q*0hZV%cuXvef>!Qw#3@|_G z=u=mTiY%B%$_}MTVc0x`Nn~%=Czw6%0kIEr)DmjiKtuY`r!y<$Rj>uS=NvbT;+@xp zGdR_Pt!E5qh(mu#v0iCe;fO@%J~u8w>aXR0`q0#%R8XP)CD{MRN%+aQ*3>@(d@|w z^hB$MbShCu#Z1KN=t(m6qK1a=tm3{be$Cv+)m?_<%efDVHTooZ%x}2K_zkWU;Wzgz zdd!tPp7`K5zlY!aoZo!mH^l6AAk42~9}DP0{l2~9k(wg^wWLht<4Ku04iK~a3Ki0% z6n!SqSVaUc8~RDc8weOiPt-gJ9G!BAIs`vy*4Z4oDv8G6$fqal6uxA1g&imXm>;I2 zOIbtLf;293xGJu&o#{bWgwPdLU;?|8^_smkC8|CMeL*E?ffAGf1Qj}yX%Cm)P=sd> zc>2LDNzrMGA{0r%Ig7*Crvg$KBp3^Ku7WQPTFk{FsVoE0RDe&39d#A<3PH-c)EVUC z2?%D$2|0q;p?t|2m^1eN8yM8U^}M39I4?q)WTJa{S&jpK)eUjt3F9imEAL{O_lPra zNMIKHaM4aUq59cZ<>PV_99NhgANLHU`MobNm9hP8|4L;sSx(bRpfdipr|dL50OZ+! z=$4T;pguF=wb}pr6u<^*2d3wA2hO+v80Jg}zJs)Y-Ofgf2kUpl8x=m`4mD92ys`%s z!BmEqOLDW$D>mr4765b)HD^^Z&W9T@E5O&IBi>UDS-pml4=IgagqOadL)i1c?)U*j z7uD|#cu&bqKbp9hH=(d39$I!yDBxl4oGZY3^@rp^%t*q`|8~A%$xsnbp+YX)ulf+! zpm(U7l|r;3@vTCho6iI$Sp;yOaF(Y+N~VYF?cx-M4^<-!&c0iQ-6s09uuQ>f4pIcE z^;_`LfMNiO1U?ECBk2rD8BCG=sW_Si`?$mnqaYO^H^Jz571X~Wf3@nIyIcugV)Dis zZdc)-SM)meYI&k?RNZVy3am%+d<;%7sCEUf8ZbVu20NL^*ptwVk|rktznUndw1vo8 z%7D{Jle$bgP9(s0=d`5KL6>cCiFt9z8lY|>arp+ZFVn7N+Bs_jBtQ&T%dUQ9PkLGX zm>5!aJN_xANpVmsdkiU!W|8%ihN@HctOu$Xo#nEkE#9XYykl*RJ!hae&gra9PG&d( ze5Hp?8(vEEIq_@}kcVBvzs~l|usMxiHEh1lXl&T9h7$1DI|oor`6Td|Q8hjSVc{e4 z?6GuM>NQZAQun*n6~-Yt*M@V@u%6o*(uDw7)2 zK9mo-i3QOiTS;HjXoi`+qFq@AI5mM{>qLGUP+Xe_G|Y48)N+tbF4Tn$5lTA` z22--qBEealN~gV^H5QqSHTJN^g+Z;73RYQXtE{lf9_R#`#blV`tGNU37Kkg`3) zlTeWLSEPCUsWNQ74eYLl%{e4t;`*FPBMCzYgCc&5=5j4kz}G$%x(S#WA&t-Ev=3(C z;;aO-^9IKihs{}_hbEIAz47hOXhWb1+iLy~xA@vr|dm}g4S zeiw5ZXnqJ&mqB^BXrb?Pcb;j zaQ;x&mQZ8r9E2jKo*y%mzsia}FRFa46}?i(tmyx$4YuuUQ*2x@j%Tw!`m#>Mg}|Yt z+->l9Hqr~(NH5ArkslAM1)UO~gD|0V@XhY9*n4X1ce^DR=%+@!!y+^D@5NWZ1`7}r z&rtL^_tbD|8JB%H3{!xM6&xTPJuFSzCBe{yikUpw4#)_*U!#pB={`;D#z zTwY~S4PfE2;&bHks!2I5UXY8Uh-*O?p84xT7qp=4IX`dV2C<8%G3>{H-*_!}xrCfz z)ZOj38a8i01M6XP_L}JByLcK58&Dx6;CJxr51aEK^J`25{9}MGJQK_Ss2g9QEqvwR z3s=leFsb}qD|7hetl*d5<2AUHtpNiV$RruxcQc#~s>NDB--$SAj>zDjJVU(aqq{i{2_UlhKmnjvpP*Tw{SVJkt)Y zhiBTsRd`0&s!KxG1vE~;tY&HkF!x6Ax;ZAuoH2Lls)CA(e1EskO^h`Q+vf@SfJ|Wl#eK2-JJGgFzSk*Mq5bP zx8)c4iN6l7njSW%15rlGo(?cNWVXBlJ6Eb5@FLXnc4 z4Uo1cuP!>F@z#n-GiadM6R&L>8#OckpS{elUBT|9yzCN}UG~!?^LuJG)-@(L$sD8F zp5k7e_R}PDooaiU%U1j}DP{Emv|sW^yo<<@(*xc~f5dAeCoUh=bk7ht(epb7vw?H~ z*!yrk!bsKjyQqD@ob=_{n+ASb@d;}C7Muk6-SCbNm;Ccow9O}-8k znl23)o?Nnhz{XMFCnVrFOgRwzIwEr50kE6yq8f2zb*=EyoR%Edtn(vZZ13 zU9@qc1u|B|Nk6kv9#CoKRd2nlUM!g6DPI7T!$nS^dj->(& z)$x=?yNVK063$~T9!e8PSWepWhRvurpYMUl7&b%9RHPxJz6ij_su%k+U3}yIG+qyf z_Ev;!>ZU)w2|pX+5`H42!P!xEt-|^o1vu7rYZ`B{3Ohn2_(Wz8Z-Yb?+bBvd6dW}l%h=Z7@z&W|dVGKDH zfe_n54sC+H=8qwPR}vfn@KoX>1A#;~yhNs3j|7m(PYNzWu}1VUa1|9EwIMT3B(Za>vDML#ygig!6=C&f2 zI*v9Fs2@tBB~He_Cm%rF3?)9*kmLD5B(+F5M2)E-EHeV~3VsVFyi**jWy<@SzERR^ zBiHdN+B>b&c!LmMMTl>s5FdvWBcxf{uS;|58YbRmgSbzqFly*Hu{y5WDBfy^;@j*} ze5<{n;(j|CZ?Y!d+@LtNCj!o@+nUCA2rb0hL2R6>LhJl-s!-r4HJ}Rh&!)5q8RNLw zyqHoS*0@4dP^wUAkWp!sZb7(m<7*V`);`5AUz18Hes?OR?Lwihjv`f2ahF1)oI>f= zlsOs{N}EBUbUR4S5oovnAW-Q%lMSzVTgy!!6Y1KCzJrOhLhI<(G+qZHeSA;NTSKJV z9GT`sy5U+xDut0FQc6aINGXxehEl3tlSpYRh*SucZo3hJjjkrx$E}Vzd&qfCUdNV% zwVOj-F8kWep>H5z367s=AD^|ta|h)g9RqHyn+V&~>%0q;FE+?VP)|03nrtH|@8DN% z>j-QeY!qQojKmh+8N>UL!PMA4fh}>o%bxp=G?v(Krq7$9=@&(UlNtc)RrC_FO2CVJ zffRxhC^=(zSxcngB7szaT6$G30W;LFi9{t}6R5|h8?cGT@QPK#pe`Z+$sNXqKNfo3 zc!pCM8c`O9L@0HuY`4lzf1a}h!T(DtO`=}#vmdHW!_i)s?%;%jI%^-hA&o0tg~IBD zO8z*lrxJqn?3^$2pv*WY7s^6ZS@o&Ka*cetg=sllOUtxw7IOV+S{_R2rgtajvNujQuOVm9`bI3rk4({h8AbQ@s)wSf z>a>-iBHg-}5<7aB(cl+yN-4x6itwcZX;!1_j)`v z_M1%7at4M*FeexGJyQNp-G=q-cn;Fh3>)8K&bYWYCosqv9iUM*yCHNzz1_o0HlPGY zJ7w=+3ym64Rq!S!ZrtMxJfsO`_8m8|Lo)a>(@h}*Ah#=({kH0we9C?YUK3~Jxf42? z;v2ldQDptg#*p8;5oGt>{>zyO>o{vNGy21d-0El`++$3>4VYD>tWLBa(itl9TV5J1 zU>>mXLJxRBG=W1L_C-%}fh2wYb{-bYoSfo$2#8A@WDS|>^?bXv^q;;+8F*3Wr zgISj$`gp4rW}TanxZ_Z_0KP0^NAm)A*at@ta9)SEUj#fy$)lf2R`E{)s>^ptNOfpPiodaWfLsx;zf(xf$cb%?;ne%xa7ld&a#=fLAbUl zy@TSJLeu#?V5UF;c>!i>)zo38R&^9J)w5|AL0-%cW=c*h+AlQ2`jDf><1*2xNbf$1$SA!1xev6xgAEbXQvoT_N8G*n$HjgpC`Nx$;Yle#8r&FY_gg#08R!( z->>Aw5D)y3!P8Ti42jF1F?nXb&p~zUH~^cHj<#XeBOE2eNnI}e%~=b3dB^1$4%A*W zY4jnybjBc$LgVtAhPQkf`#NK9$#8-mN5+%HL)toxK8$y(I3Nsgw1gxhbH*+r81v9WlNjlxJhQc6H~ej-vP$GTF=vCrdq zwgiwX$U;Pxtn2tp?!wIyE=Ic^TxP~0$+(at@gCNeBy-uF%0du(#Io(FT!2kynKM}S zab)gdNk+;$1D=m086B5HBi3pN4)b;;@G#6B@ANlQM8EAlY6z4FG#s(q3}&!83HKOZ4l{qncqI^HD@bG zo0m0VtvCTg}m}o-e|eoneND<=vii`73}`W*y$n; zC*=Hc2Sxax>|MdV(b<(u}*? z_MXeaeL30!nuYV5N7dmTrJjE6&IlUtK83xWsmj7Z>q9S;r;I@g9iGGH)7M{LQF6t;^Nc*liz2C3$+``Edm-C?D2qAZqGs@aK?%do)#m}95f1k=CHF(S@$w`Xv4 z9}YOGn=^<~;?xOFeZzh$K6MEx4%wkn+<^h}O`n|!XE@?n&mx}aC?TRyu$SNZ>)-zc zKADk%2A%wvc>`x8n_+XdBK<-FcqNUV$HAHC*OfA2z6D%z4Ov?U17O&h>)Fp;Llaw=MU*)0wub}6xQ$iiAx44d1d!91R{S<2moF~`Y4i*4N-s5C4MHl|U;g9&@`eY`bHV=3KmF6nai-)SPWfM0 zP5C&o_A*dADf^{LI-nV4DSIWrsU*sNrKjv2J|0HdU#c)&Kv9qy(Eq1A;$bArK?TVL z0JFeH{bB|2)FZw?(A6NT{Xj|jfL}^!1SG{gp_;%AQ;xhD-pbZe1w_YfstLgV)5nf(4iBRwDE`S zc6()5Huh{9HeUlV{*Pb7Rta3)ZMT!&A`RkfG5ZH?I0eu$pP1@#w#6Agjh-Psbo(2U ze{rgcfb_pLa^mip%v~U8JNg2PNV8MrdE6I(?a0{g=X=H2D#kH(J^RNDV{d@E?mjbs z88yL%E~j+s=nJU%q#U6qHg6X$<0OX+-$5v3RwTPyjQu5@i5T_-82fK}3Rq+QR<{7E zf1D9Iy0FaWi(+)<$MvI%q>;|?=!QJ3zoOqiVdoL_SyDaf>5RzEl zowgzD;?K zNj+>pBmZZUTP)_)^==vY26`>yX_+0RElz*rBbrCE6$L2`WjCOqdUnI>2VV&vvj}(B zMLxmX{*a}C=SxR}qFP`~BlO^!?=H?K@*29yaao=smRG@-=_xr?nX{=#M_=MZCOK&_ zDyi@wpC*N5MdVLN#DxPxy!jRTRts?R*rf-><%mDJfc^V*Y+m6&6HZ0TGcWiYLf(Fd zoGY)pOVxQbEk%P@9puQzNQo_JgYH3&|MIT2gCD7Do3nGVfQ{}Uy zm(?7^FiE0T$b9{{?33f|SuSIt-R5J>v7lzr>ehW4XZ9GoF102aNlC&qe6cEBy4cMCKYCpny(SXHz!|C0-}^wBTSGO_M;_fd2{sLab}!qM;`dUD-drpTmDH#L6+9 zDd_+>Dsa<)k@uuG<vz03!HYWeMA7km^nv&xhJ@RM5@YOr|7`lZ& z+nk%6KRUPAY&EB7dhXeY+36{oXgzcEP;+i^fu5dk(L!^!Il0)JqC?Gvg^6dH*ZRUe za_`Q!l)c#Y!+2tGv3cn5A}!9-!qEeZt%=D+dTx60Ak9D9Y&|_Y|D3X?Ui8Di@CHA< z&}=OX9&R2!HZe0jH#pUNcJS~-YkKk6;Nhu-*}=*AR&#KnH92_T*kW_v(Z%W6gQo|#@ec=W)o$@xP&Kir(09KQSBd#4WE z+ngM|d$$|-&dK?y=H$-ZyLRsy;!pT~S8G8jrFtfIVrpv5oGPW>YZv6tW$imUH@ zb70hwJ+`K^cBSKoFY5n?9ll;i&b;i06YsSj z&o!I-CT3^nCwbO(^TJIZI=t_>iG@SE@5bcWl`pTJnCng8`7i&3Ow7fTe)#ob&yxRG zNbXuQm(v^m+8zE;N`0vU>8E5%nY;)zo2vU~3<@|im6e#`P)Roweu zb@yMD=j~LeLHK})=Do7W59{bOG|}9|EHzuL`PLXUon|^Z z*J@5o9-KHZ+a#8Q6Ne`zrx%aqiUhi0cKQIb-e7CuxqZ(zAuC<6sE^s{$>!jp`Kev4 zg`a~~*@xn>v4iZfbh&8JZwLC(Su1(HjK}H#CzIv#sXD)G<0R z-)hZ2*PQC@-NLcC$$gW}+1atN2PP&DHus?Uv9V{G-AP$&O*f}NAI~%wY4Kna9X;Bl zr(5%fXo9AisDdW(@sHl^=IrcX-i2MQysL+fE>0Yn?RNP_Ktb#1!s6s$bN4-Wk4_yJ znY{bJhnvj>_;&6RnKh4k4 z1nvLi{9JRtjHp*M&GYLwfM^fRPc>&98k}Q1&5+{sX+QkS(!E-%?MkUXQ(6rxrCwG_ z6%_w1SQRU!PPy`b4S=YWdRu9=RVlTxq*cFC>P=6nno_D?%llb9pe@w1wtTNE|I?yY zb*0qjwO0F-QXf)Em6cLwwbXx6YxQ}h)GsQfib|<*BlY)|qyEFLe8tH7wqdH{%29E^-8H96g98M-x;Bw zc1gx{rli#;l~UU@T(wGlE6{2j^cYC_pDM-QuLLsQ?EODcO5LuM`fORN&nTt-eOar9 zQtFS~`_ET|4zludt~{zpJwp|(9#TsEJEM5*heGMk)=&e*sV9}p=U-Mdug~R*l)rGD t@~^q_we|eGD_5bko7E5A0DrA5KjO-NBh>0HrPR4ltBp#j&uFDK{$IBuLhS$m literal 0 HcmV?d00001 diff --git a/packages/api/test/e2e/promise-alex.spec.js b/packages/api/test/e2e/promise-alex.spec.js new file mode 100644 index 0000000000..d2ebf11688 --- /dev/null +++ b/packages/api/test/e2e/promise-alex.spec.js @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Api from '../../src/promise'; +import WsProvider from '../../../rpc-provider/src/ws'; + +describe.skip('alex queries', () => { + let api; + + beforeAll(async () => { + api = await Api.create({ + provider: new WsProvider('wss://poc3-rpc.polkadot.io/') + }); + + return api; + }); + + beforeEach(() => { + jest.setTimeout(3000000); + }); + + it('retrieves the list of validators', (done) => { + api.query.staking.validators((res) => { + console.log('api.query.staking.validators():', res.toJSON()); + + done(); + }); + }); + + it('retrieves a single value', (done) => { + api.query.staking.validators('5EF7wdkP9XZq38fu2ioAvxoEhHUJUc1E2KbuSQSMGofRKhCL', (res) => { + console.log('api.query.staking.validators(id):', res.toJSON()); + + done(); + }); + }); + + it('derives a list of the controllers', (done) => { + api.derive.staking.controllers((res) => { + console.log('api.derive.staking.controllers:', JSON.stringify(res)); + + done(); + }); + }); + + it.skip('retrieves the list of nominators', (done) => { + let count = 0; + api.query.staking.nominators((res) => { + console.log(`[${++count}]:: nominators(${res[0].length}):`, res.toJSON()); + + // done(); + }); + }); +}); diff --git a/packages/api/test/e2e/promise-contract.spec.js b/packages/api/test/e2e/promise-contract.spec.js new file mode 100644 index 0000000000..3583325a00 --- /dev/null +++ b/packages/api/test/e2e/promise-contract.spec.js @@ -0,0 +1,92 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import fs from 'fs'; +import path from 'path'; + +import { ContractAbi } from '@polkadot/types'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import json from '../data/erc20.json'; +import Api from '../../src/promise'; + +describe.skip('e2e contracts', () => { + let address; + let codeHash; + let keyring; + let abi; + let api; + + beforeEach(async (done) => { + if (!api) { + api = await Api.create(); + + abi = new ContractAbi(json); + keyring = testingPairs({ type: 'sr25519' }); + } + + jest.setTimeout(10000); + done(); + }); + + it('has the attached methods', () => { + expect(Object.keys(abi.messages)).toEqual( + ['totalSupply', 'balanceOf', 'allowance', 'transfer', 'approve', 'transferFrom'] + ); + }); + + it('allows putCode', (done) => { + const code = fs.readFileSync(path.join(__dirname, '../data/erc20.wasm')).toString('hex'); + + api.tx.contract + .putCode(200000, `0x${code}`) + .signAndSend(keyring.eve, (result) => { + console.error('putCode', JSON.stringify(result)); + + if (result.status.isFinalized) { + const record = result.findRecord('contract', 'CodeStored'); + + if (record) { + codeHash = record.event.data[0]; + + done(); + } + } + }); + }); + + it('allows contract create', (done) => { + expect(codeHash).toBeDefined(); + + api.tx.contract + .create(1234, 12345, codeHash, abi.deploy(12345)) + .signAndSend(keyring.bob, (result) => { + console.error('create', JSON.stringify(result)); + + if (result.status.isFinalized) { + const record = result.findRecord('contract', 'Instantiated'); + + if (record) { + address = record.event.data[1]; + + done(); + } + } + }); + }); + + it('allows contract call', (done) => { + expect(address).toBeDefined(); + + api.tx.contract + .call(address, 123, 12345, abi.messages.balanceOf(keyring.alice.address())) + .signAndSend(keyring.bob, (result) => { + console.error('call', JSON.stringify(result)); + + if (result.status.isFinalized && result.findRecord('system', 'ExtrinsicSuccess')) { + done(); + } + }); + }); +}); diff --git a/packages/api/test/e2e/promise-queries.spec.js b/packages/api/test/e2e/promise-queries.spec.js new file mode 100644 index 0000000000..4ec9c9b21b --- /dev/null +++ b/packages/api/test/e2e/promise-queries.spec.js @@ -0,0 +1,87 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import Api from '../../src/promise'; + +describe.skip('e2e queries', () => { + const keyring = testingPairs({ type: 'ed25519' }); + let api; + + beforeEach(async (done) => { + if (!api) { + api = await Api.create(); + } + + jest.setTimeout(30000); + done(); + }); + + it('makes the runtime, rpc, state & extrinsics available', () => { + expect(api.genesisHash).toBeDefined(); + expect(api.runtimeMetadata).toBeDefined(); + expect(api.runtimeVersion).toBeDefined(); + expect(api.rpc).toBeDefined(); + expect(api.query).toBeDefined(); + expect(api.tx).toBeDefined(); + expect(api.derive).toBeDefined(); + }); + + it('queries state for a balance', async () => { + const balance = await api.query.balances.freeBalance(keyring.alice.address()); + + expect( + balance.isZero() + ).toBe(false); + }); + + it('subscribes to rpc', (done) => { + api.rpc.chain.subscribeNewHead((header) => { + expect(header.blockNumber.isZero()).toBe(false); + + done(); + }); + }); + + it('subscribes to finalized', (done) => { + api.rpc.chain.subscribeFinalizedHeads((header) => { + expect(header.blockNumber.isZero()).toBe(false); + + done(); + }); + }); + + it('subscribes to derive', (done) => { + api.derive.chain.subscribeNewHead((header) => { + expect(header.blockNumber.isZero()).toBe(false); + + done(); + }); + }); + + it('subscribes to queries', (done) => { + api.query.system.accountNonce(keyring.ferdie.address(), (nonce) => { + expect(nonce instanceof BN).toBe(true); + + done(); + }); + }); + + it.skip('subscribes to queries (default)', (done) => { + api.query.staking.validators(keyring.ferdie.address(), (prefs) => { + expect(prefs.unstakeThreshold.toNumber()).toBe(3); + + done(); + }); + }); + + it('makes a query at a specific block', async () => { + const header = await api.rpc.chain.getHeader(); + const events = await api.query.system.events.at(header.hash); + + expect(events.length).not.toEqual(0); + }); +}); diff --git a/packages/api/test/e2e/promise-tx.spec.js b/packages/api/test/e2e/promise-tx.spec.js new file mode 100644 index 0000000000..6566161a19 --- /dev/null +++ b/packages/api/test/e2e/promise-tx.spec.js @@ -0,0 +1,214 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Keyring from '@polkadot/keyring'; +import testingPairs from '@polkadot/keyring/testingPairs'; +import { randomAsHex } from '@polkadot/util-crypto'; + +import Api from '../../src/promise'; +import WsProvider from '../../../rpc-provider/src/ws'; +import SingleAccountSigner from "../util/SingleAccountSigner"; + +describe.skip('e2e transactions', () => { + const keyring = testingPairs({ type: 'ed25519' }); + let api; + + beforeEach(async (done) => { + if (!api) { + api = await Api.create({ + provider: new WsProvider('ws://127.0.0.1:9944') + }); + } + + jest.setTimeout(30000); + done(); + }); + + afterEach(() => { + jest.setTimeout(5000); + }); + + it('makes a transfer (sign, then send)', async (done) => { + const nonce = await api.query.system.accountNonce(keyring.dave.address()); + + return api.tx.balances + .transfer(keyring.eve.address(), 12345) + .sign(keyring.dave, { nonce }) + .send(({ events, status }) => { + console.log('Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + if (events.length) { + done(); + } + } + }); + }); + + it('makes a transfer (sign, then send - compat version)', async (done) => { + const nonce = await api.query.system.accountNonce(keyring.dave.address()); + + return api.tx.balances + .transfer(keyring.eve.address(), 12345) + .sign(keyring.dave, nonce) + .send(({ events, status }) => { + console.log('Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + if (events.length) { + done(); + } + } + }); + }); + + it('makes a transfer (signAndSend)', async (done) => { + return api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.dave, ({ events, status }) => { + console.log('Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + if (events.length) { + done(); + } + } + }); + }); + + it('makes a transfer (signAndSend via Signer)', async (done) => { + const signer = new SingleAccountSigner(keyring.dave); + + api.setSigner(signer); + + return api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.dave.address(), ({ events, status }) => { + console.log('Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + if (events.length) { + done(); + } + } + }); + }); + + it('makes a transfer (signAndSend via Signer) - sad path', async () => { + //no signer + api.setSigner(); + + await expect(api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.alice.address())).rejects.toThrow('no signer exists'); + + const signer = new SingleAccountSigner(keyring.dave); + + api.setSigner(signer); + + //no callback + await expect(api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.alice.address())).rejects.toThrow('does not have the keyringPair'); + + //with callback + await expect(api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.alice.address(), ({ events, status }) => { + })).rejects.toThrow('does not have the keyringPair'); + }); + + it('makes a transfer (no callback)', async () => { + const hash = await api.tx.balances + .transfer(keyring.eve.address(), 12345) + .signAndSend(keyring.dave); + + expect(hash.toHex()).toHaveLength(66); + }); + + it('makes a proposal', async () => { + // don't wait for status, just get hash. Here we generate a large-ish payload + // to ensure that we can sign with the hashed version as well (and have it accepted) + const hash = await api.tx.democracy + .propose(api.tx.consensus.setCode(randomAsHex(4096)), 10000) + .signAndSend(keyring.bob); + + expect(hash.toHex()).toHaveLength(66); + }); + + it('makes a transfer, and uses new balance to transfers to new', async (done) => { + const pair = new Keyring().addFromUri('testing123', {}, 'ed25519'); + + function doOne (cb) { + return api.tx.balances + .transfer(pair.address(), 123456) + .signAndSend(keyring.dave, ({ events, status }) => { + console.log('One: Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + cb() + } + }); + }; + + function doTwo (cb) { + return api.tx.balances + .transfer(keyring.alice.address(), 12345) + .signAndSend(pair, ({ events, status }) => { + console.log('One: Transaction status:', status.type); + + if (status.isFinalized) { + console.log('Completed at block hash', status.value.toHex()); + console.log('Events:'); + + events.forEach(({ phase, event: { data, method, section } }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + + cb() + } + }); + } + + // return doTwo(done); + return doOne(() => { + doTwo(done) + }); + }); +}); diff --git a/packages/api/test/e2e/rx-queries.spec.js b/packages/api/test/e2e/rx-queries.spec.js new file mode 100644 index 0000000000..1796f7a4a4 --- /dev/null +++ b/packages/api/test/e2e/rx-queries.spec.js @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { switchMap } from 'rxjs/operators'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import Api from '../../src/rx'; + +describe.skip('e2e queries', () => { + const keyring = testingPairs({ type: 'ed25519' }); + let api; + + beforeEach(async (done) => { + api = await Api.create().toPromise(); + jest.setTimeout(3000000); + done(); + }); + + it('makes the runtime, rpc, state & extrinsics available', () => { + expect(api.genesisHash).toBeDefined(); + expect(api.runtimeMetadata).toBeDefined(); + expect(api.runtimeVersion).toBeDefined(); + expect(api.rpc).toBeDefined(); + expect(api.query).toBeDefined(); + expect(api.tx).toBeDefined(); + expect(api.derive).toBeDefined(); + }); + + it('queries state for a balance', (done) => { + api.query.balances.freeBalance(keyring.alice.address()).subscribe((balance) => { + expect( + balance.isZero() + ).toBe(false); + + done(); + }); + }); + + it('makes a query at a specific block', (done) => { + api.rpc.chain + .getHeader() + .pipe( + switchMap((header) => + api.query.system.events.at(header.hash) + ) + ) + .subscribe((events) => { + expect(events.length).not.toEqual(0); + done(); + }); + }); +}); diff --git a/packages/api/test/e2e/rx-tx.spec.js b/packages/api/test/e2e/rx-tx.spec.js new file mode 100644 index 0000000000..98957602f9 --- /dev/null +++ b/packages/api/test/e2e/rx-tx.spec.js @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { first, switchMap } from 'rxjs/operators'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import Api from '../../src/rx'; + +describe.skip('e2e transactions', () => { + const keyring = testingPairs({ type: 'ed25519' }); + let api; + + beforeEach(async (done) => { + api = await Api.create().toPromise(); + jest.setTimeout(30000); + done() + }); + + afterEach(() => { + jest.setTimeout(5000); + }); + + it('makes a transfer', (done) => { + api.query.system + .accountNonce(keyring.alice.address()) + .pipe( + first(), + switchMap((nonce) => + api.tx.balances + .transfer(keyring.bob.address(), 12345) + .sign(keyring.alice, { nonce }) + .send() + ) + ) + .subscribe(({ status }) => { + if (status.isFinalized) { + done(); + } + }); + }); + + it('makes a proposal', (done) => { + api.query.system + .accountNonce(keyring.alice.address()) + .pipe( + first(), + switchMap((nonce) => + api.tx.democracy + .propose(api.tx.consensus.setCode('0xdeadbeef'), 10000) + .sign(keyring.alice, { nonce }) + .send() + ) + ) + .subscribe(({ status }) => { + if (status.isFinalized) { + done(); + } + }); + }); +}); diff --git a/packages/api/test/util/SingleAccountSigner.ts b/packages/api/test/util/SingleAccountSigner.ts new file mode 100644 index 0000000000..c44eb7cd40 --- /dev/null +++ b/packages/api/test/util/SingleAccountSigner.ts @@ -0,0 +1,28 @@ +// Copyright 2017-2019 @polkadot/api authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import { SignatureOptions } from '@plugnet/types/types'; + +import { Extrinsic } from '@plugnet/types'; + +let id = 0; + +export default class SingleAccountSigner { + private keyringPair: KeyringPair; + + constructor (keyringPair: KeyringPair) { + this.keyringPair = keyringPair; + } + + async sign (extrinsic: Extrinsic, address: string, options: SignatureOptions) { + if (!this.keyringPair || String(address) !== this.keyringPair.address()) { + throw new Error('does not have the keyringPair'); + } + + extrinsic.sign(this.keyringPair, options); + + return ++id; + } +} diff --git a/packages/keyring/LICENSE b/packages/keyring/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/keyring/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/keyring/README.md b/packages/keyring/README.md new file mode 100644 index 0000000000..3ad57647fb --- /dev/null +++ b/packages/keyring/README.md @@ -0,0 +1,21 @@ +# @polkadot/keyring + +Key management of user accounts including generation and retrieval of keyring pairs from a variety of input combinations. + +## Usage + +Installation - + +``` +yarn add @polkadot/keyring +``` + +Classes and Functions can be imported as follows: + +```js +import Keyring from '@polkadot/keyring'; +``` + +## Utilities + +[Utilities](SUMMARY.md) diff --git a/packages/keyring/package.json b/packages/keyring/package.json new file mode 100644 index 0000000000..2af5883a19 --- /dev/null +++ b/packages/keyring/package.json @@ -0,0 +1,39 @@ +{ + "name": "@plugnet/keyring", + "version": "0.76.1", + "description": "Keyring management", + "main": "index.js", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/common.git" + }, + "keywords": [ + "Plugnet", + "Keyring", + "ed25519" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/polkadot-js/common/issues" + }, + "homepage": "https://github.com/polkadot-js/common/tree/master/packages/keyring#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/util": "^0.76.1", + "@plugnet/util-crypto": "^0.76.1", + "@types/bs58": "^4.0.0", + "bs58": "^4.0.1" + } +} diff --git a/packages/keyring/src/address/decode.spec.ts b/packages/keyring/src/address/decode.spec.ts new file mode 100644 index 0000000000..f985de54bd --- /dev/null +++ b/packages/keyring/src/address/decode.spec.ts @@ -0,0 +1,90 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. +// @flow + +import testingPairs from '../testingPairs'; +import decode from './decode'; + +describe('decode', () => { + let keyring: any; + + beforeAll(async () => { + keyring = testingPairs({ type: 'sr25519' }); + }); + + it('decodes an address', () => { + expect( + decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY') + ).toEqual( + keyring.alice.publicKey() + ); + }); + + it('converts a publicKey (u8a) as-is', () => { + expect( + decode(new Uint8Array([1, 2, 3])) + ).toEqual( + new Uint8Array([1, 2, 3]) + ); + }); + + it('converts a publicKey (hex) as-is', () => { + expect( + decode('0x01020304') + ).toEqual( + new Uint8Array([1, 2, 3, 4]) + ); + }); + + it('decodes a short address', () => { + expect( + decode('F7NZ') + ).toEqual(new Uint8Array([1])); + }); + + it('decodes a 1-byte accountId (with prefix)', () => { + expect( + decode('PqtB', false, 68) + ).toEqual(new Uint8Array([1])); + }); + + it('decodes a 2-byte accountId', () => { + expect( + decode('2jpAFn', false, 68) + ).toEqual(new Uint8Array([0, 1])); + }); + + it('encodes a 4-byte address', () => { + expect( + decode('as7QnGMf', false, 68) + ).toEqual(new Uint8Array([1, 2, 3, 4])); + }); + + it('decodes a 8-byte address', () => { + expect( + decode('4q7qY5RBG7Z4wv', false, 68) + ).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0])); + }); + + it.skip('allows invalid prefix (in list)', () => { + expect( + () => decode('6GfvWUvHvU8otbZ7sFhXH4eYeMcKdUkL61P3nFy52efEPVUx') + ).toThrow(/address prefix/); + }); + + it('fails when length is invalid', () => { + expect( + () => decode('y9EMHt34JJo4rWLSaxoLGdYXvjgSXEd4zHUnQgfNzwES8b') + ).toThrow(/address length/); + }); + + it('fails when the checksum does not match', () => { + expect( + () => decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMa9cj') + ).toThrow(/address checksum/); + expect( + () => decode('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDwU') + ).toThrow(/address checksum/); + }); +}); diff --git a/packages/keyring/src/address/decode.ts b/packages/keyring/src/address/decode.ts new file mode 100644 index 0000000000..7f4d74b015 --- /dev/null +++ b/packages/keyring/src/address/decode.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Prefix } from './types'; + +// Original implementation: https://github.com/paritytech/polka-ui/blob/4858c094684769080f5811f32b081dd7780b0880/src/polkadot.js#L6 + +import bs58 from 'bs58'; +import { assert, bufferToU8a, isHex, isU8a, u8aToU8a } from '@plugnet/util'; + +import defaults from './defaults'; +import sshash from './sshash'; + +export default function decode (encoded: string | Uint8Array, ignoreChecksum?: boolean, prefix: Prefix = defaults.prefix): Uint8Array { + if (isU8a(encoded) || isHex(encoded)) { + return u8aToU8a(encoded); + } + + const decoded = bufferToU8a(bs58.decode(encoded)); + const error = (message: string) => + `Decoding ${encoded}: ${message}`; + + // assert(defaults.allowedPrefix.includes(decoded[0] as Prefix), error('Invalid decoded address prefix')); + assert(defaults.allowedEncodedLengths.includes(decoded.length), error('Invalid decoded address length')); + + // TODO Unless it is an "use everywhere" prefix, throw an error + // if (decoded[0] !== prefix) { + // console.log(`WARN: Expected ${prefix}, found ${decoded[0]}`); + // } + + const isPublicKey = decoded.length === 35; + + // non-publicKeys has 1 byte checksums, else default to 2 + const endPos = decoded.length - (isPublicKey ? 2 : 1); + + // calculate the hash and do the checksum byte checks + const hash = sshash(decoded.subarray(0, endPos)); + const checks = isPublicKey + ? decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1] + : decoded[decoded.length - 1] === hash[0]; + + assert(ignoreChecksum || checks, error('Invalid decoded address checksum')); + + return decoded.slice(1, endPos); +} diff --git a/packages/keyring/src/address/defaults.ts b/packages/keyring/src/address/defaults.ts new file mode 100644 index 0000000000..0d7861c2b3 --- /dev/null +++ b/packages/keyring/src/address/defaults.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Prefix } from './types'; + +const defaults = { + allowedDecodedLengths: [1, 2, 4, 8, 32], + // publicKey has prefix + 2 checksum bytes, short only prefix + 1 checksum byte + allowedEncodedLengths: [3, 4, 6, 10, 35], + allowedPrefix: [0, 1, 3, 42, 43, 68, 69] as Array, + prefix: 42 as Prefix +}; + +export default defaults; diff --git a/packages/keyring/src/address/encode.spec.ts b/packages/keyring/src/address/encode.spec.ts new file mode 100644 index 0000000000..2e39dfb24b --- /dev/null +++ b/packages/keyring/src/address/encode.spec.ts @@ -0,0 +1,65 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. +// @flow + +import testingPairs from '../testingPairs'; +import encode from './encode'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('encode', () => { + it('encodes an address to a valid value', () => { + expect( + keyring.alice.address() + ).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua'); + }); + + it('fails when non-valid publicKey provided', () => { + expect( + () => encode( + keyring.alice.publicKey().slice(0, 30) + ) + ).toThrow(/Expected a valid key/); + }); + + it('encodes a 1-byte address', () => { + expect( + encode( + new Uint8Array([1]) + ) + ).toEqual('F7NZ'); + }); + + it('encodes a 1-byte address (with prefix)', () => { + expect( + encode( + new Uint8Array([1]), 68 + ) + ).toEqual('PqtB'); + }); + + it('encodes a 2-byte address', () => { + expect( + encode( + new Uint8Array([0, 1]), 68 + ) + ).toEqual('2jpAFn'); + }); + + it('encodes a 4-byte address', () => { + expect( + encode( + new Uint8Array([1, 2, 3, 4]), 68 + ) + ).toEqual('as7QnGMf'); + }); + + it('enodes a 8-byte address', () => { + expect( + encode( + new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 68 + ) + ).toEqual('4q7qY5RBG7Z4wv'); + }); +}); diff --git a/packages/keyring/src/address/encode.ts b/packages/keyring/src/address/encode.ts new file mode 100644 index 0000000000..37ed47567f --- /dev/null +++ b/packages/keyring/src/address/encode.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Prefix } from './types'; + +// Original implementation: https://github.com/paritytech/polka-ui/blob/4858c094684769080f5811f32b081dd7780b0880/src/polkadot.js#L34 + +import bs58 from 'bs58'; +import { assert, u8aConcat, u8aToBuffer, u8aToU8a } from '@plugnet/util'; + +import defaults from './defaults'; +import sshash from './sshash'; + +export default function encode (_key: Uint8Array | string, prefix: Prefix = defaults.prefix): string { + const key = u8aToU8a(_key); + + assert(defaults.allowedDecodedLengths.includes(key.length), `Expected a valid key to convert, with length ${defaults.allowedDecodedLengths}`); + + const isPublicKey = key.length === 32; + + const input = u8aConcat(new Uint8Array([prefix]), key); + const hash = sshash(input); + + return bs58.encode( + u8aToBuffer( + u8aConcat(input, hash.subarray(0, isPublicKey ? 2 : 1)) + ) + ); +} diff --git a/packages/keyring/src/address/index.ts b/packages/keyring/src/address/index.ts new file mode 100644 index 0000000000..5197a7169c --- /dev/null +++ b/packages/keyring/src/address/index.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import decodeAddress from './decode'; +import encodeAddress from './encode'; +import setAddressPrefix from './setPrefix'; + +export { + decodeAddress, + encodeAddress, + setAddressPrefix +}; diff --git a/packages/keyring/src/address/setPrefix.spec.ts b/packages/keyring/src/address/setPrefix.spec.ts new file mode 100644 index 0000000000..8e915f2feb --- /dev/null +++ b/packages/keyring/src/address/setPrefix.spec.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import encode from './encode'; +import setPrefix from './setPrefix'; + +describe('setPrefix', () => { + beforeEach(() => { + setPrefix(68); + }); + + it('sets and allows encoding using', () => { + expect( + encode( + new Uint8Array([1]) + ) + ).toEqual('PqtB'); + }); +}); diff --git a/packages/keyring/src/address/setPrefix.ts b/packages/keyring/src/address/setPrefix.ts new file mode 100644 index 0000000000..a2d9c71351 --- /dev/null +++ b/packages/keyring/src/address/setPrefix.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Prefix } from './types'; + +import defaults from './defaults'; + +export default function setPrefix (prefix: Prefix): void { + defaults.prefix = prefix; +} diff --git a/packages/keyring/src/address/sshash.ts b/packages/keyring/src/address/sshash.ts new file mode 100644 index 0000000000..1003d6f2b5 --- /dev/null +++ b/packages/keyring/src/address/sshash.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a, u8aConcat } from '@plugnet/util'; +import { blake2AsU8a } from '@plugnet/util-crypto'; + +const SS58_PREFIX = stringToU8a('SS58PRE'); + +export default function hash (key: Uint8Array): Uint8Array { + return blake2AsU8a(u8aConcat(SS58_PREFIX, key), 512); +} diff --git a/packages/keyring/src/address/types.ts b/packages/keyring/src/address/types.ts new file mode 100644 index 0000000000..02aaf6165f --- /dev/null +++ b/packages/keyring/src/address/types.ts @@ -0,0 +1,5 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type Prefix = 0 | 1 | 3 | 42 | 43 | 68 | 69; diff --git a/packages/keyring/src/defaults.ts b/packages/keyring/src/defaults.ts new file mode 100644 index 0000000000..d6044488bc --- /dev/null +++ b/packages/keyring/src/defaults.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// default substrate dev phrase +const DEV_PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk'; + +// seed from the above phrase +const DEV_SEED = '0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e'; + +export { + DEV_PHRASE, + DEV_SEED +}; diff --git a/packages/keyring/src/index.spec.ts b/packages/keyring/src/index.spec.ts new file mode 100644 index 0000000000..63bd4dbcbd --- /dev/null +++ b/packages/keyring/src/index.spec.ts @@ -0,0 +1,149 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a, stringToU8a } from '@plugnet/util'; +import { cryptoWaitReady } from '@plugnet/util-crypto'; + +import Keyring from '.'; +import setPrefix from './address/setPrefix'; + +describe('keypair', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + describe('ed25519', () => { + const publicKeyOne = new Uint8Array([47, 140, 97, 41, 216, 22, 207, 81, 195, 116, 188, 127, 8, 195, 230, 62, 209, 86, 207, 120, 174, 251, 74, 101, 80, 217, 123, 135, 153, 121, 119, 238]); + const publicKeyTwo = new Uint8Array([215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114, 243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26]); + const seedOne = stringToU8a('12345678901234567890123456789012'); + const seedTwo = hexToU8a('0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'); + let keypair: Keyring; + + beforeEach(async () => { + keypair = new Keyring({ type: 'ed25519' }); + + keypair.addFromSeed(seedOne, {}); + }); + + it('adds the pair', () => { + expect( + keypair.addFromSeed(seedTwo, {}).publicKey() + ).toEqual(publicKeyTwo); + }); + + it('creates a ed25519 pair via mnemonicToSeed', () => { + expect( + keypair.addFromUri( + 'seed sock milk update focus rotate barely fade car face mechanic mercy' + ).address() + ).toEqual('5DkQP32jP4DVJLWWBRBoZF2tpWjqFrcrTBo6H5NcSk7MxKCC'); + }); + + it('adds from a mnemonic', () => { + setPrefix(68); + + expect( + keypair.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address() + ).toEqual('7sPsxWPE5DzAyPT3VuoJYw5NTGscx9QYN9oddQx4kALKC3hH'); + }); + + it('allows publicKeys retrieval', () => { + keypair.addFromSeed(seedTwo, {}); + + expect( + keypair.getPublicKeys() + ).toEqual([ publicKeyOne, publicKeyTwo ]); + }); + + it('allows retrieval of a specific item', () => { + expect( + keypair.getPair(publicKeyOne).publicKey() + ).toEqual(publicKeyOne); + }); + + it('allows adding from JSON', () => { + expect( + keypair.addFromJson( + JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}') + ).publicKey() + ).toEqual( + new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]) + ); + }); + + it('signs and verifies', () => { + const MESSAGE = stringToU8a('this is a message'); + const pair = keypair.getPair(publicKeyOne); + const signature = pair.sign(MESSAGE); + + expect(pair.verify(MESSAGE, signature)).toBe(true); + }); + }); + + describe('sr25519', () => { + const publicKeyOne = new Uint8Array([116, 28, 8, 160, 111, 65, 197, 150, 96, 143, 103, 116, 37, 155, 217, 4, 51, 4, 173, 250, 93, 62, 234, 98, 118, 11, 217, 190, 151, 99, 77, 99]); + const publicKeyTwo = hexToU8a('0x44a996beb1eef7bdcab976ab6d2ca26104834164ecf28fb375600576fcc6eb0f'); + const seedOne = stringToU8a('12345678901234567890123456789012'); + const seedTwo = hexToU8a('0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60'); + let keypair: Keyring; + + beforeEach(() => { + keypair = new Keyring({ type: 'sr25519' }); + + keypair.addFromSeed(seedOne, {}); + }); + + it('creates with dev phrase when only path specified', () => { + expect( + keypair.createFromUri('//Alice').address() + ).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); + }); + + it('adds the pair', () => { + expect( + keypair.addFromSeed(seedTwo, {}).publicKey() + ).toEqual(publicKeyTwo); + }); + + it('adds from a mnemonic', () => { + setPrefix(68); + + expect( + keypair.addFromMnemonic('moral movie very draw assault whisper awful rebuild speed purity repeat card', {}).address() + ).toEqual('7qQGarA4PWjPPVHG4USn1yuuVZvEHN7XZz8o7EbAp48jayZQ'); + }); + + it('allows publicKeys retrieval', () => { + keypair.addFromSeed(seedTwo, {}); + + expect( + keypair.getPublicKeys() + ).toEqual([ publicKeyOne, publicKeyTwo ]); + }); + + it('allows retrieval of a specific item', () => { + expect( + keypair.getPair(publicKeyOne).publicKey() + ).toEqual(publicKeyOne); + }); + + it('allows adding from JSON', () => { + expect( + keypair.addFromJson( + JSON.parse('{"address":"5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua","encoded":"0xb4a14995d25ab609f3686e9fa45f1fb237cd833f33f00d4b12c51858ca070d96972e47d73aae5eeb0fc06f923826cf0943fdb02c2c2ee30ef52a7912663053940d1da4da66b3a3f520ae07422c1c94b2d95690fca9d1f4a997623bb2923a8833280e19e7f72c3c5cfa343974e60e2b3dc53b404fdaf330756daad5e4e3","encoding":{"content":"pkcs8","type":"xsalsa20-poly1305","version":"0"},"meta":{"isTesting":true,"name":"alice"}}') + ).publicKey() + ).toEqual( + new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]) + ); + }); + + it('signs and verifies', () => { + const MESSAGE = stringToU8a('this is a message'); + const pair = keypair.getPair(publicKeyOne); + const signature = pair.sign(MESSAGE); + + expect(pair.verify(MESSAGE, signature)).toBe(true); + }); + }); +}); diff --git a/packages/keyring/src/index.ts b/packages/keyring/src/index.ts new file mode 100644 index 0000000000..45a0de8b70 --- /dev/null +++ b/packages/keyring/src/index.ts @@ -0,0 +1,242 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeypairType } from '@plugnet/util-crypto/types'; +import { KeyringInstance, KeyringPair, KeyringPair$Json, KeyringPair$Meta, KeyringOptions } from './types'; + +import { assert, assertSingletonPackage, hexToU8a, isNumber, isHex, stringToU8a } from '@plugnet/util'; +import { keyExtractSuri, naclKeypairFromSeed as naclFromSeed, schnorrkelKeypairFromSeed as schnorrkelFromSeed, mnemonicToMiniSecret, keyFromPath } from '@plugnet/util-crypto'; + +import { decodeAddress, encodeAddress, setAddressPrefix } from './address'; +import { DEV_PHRASE } from './defaults'; +import createPair from './pair'; +import Pairs from './pairs'; + +assertSingletonPackage('@plugnet/keyring'); + +/** + * # @plugnet/keyring + * + * ## Overview + * + * @name Keyring + * @summary Keyring management of user accounts + * @description Allows generation of keyring pairs from a variety of input combinations, such as + * json object containing account address or public key, account metadata, and account encoded using + * `addFromJson`, or by providing those values as arguments separately to `addFromAddress`, + * or by providing the mnemonic (seed phrase) and account metadata as arguments to `addFromMnemonic`. + * Stores the keyring pairs in a keyring pair dictionary. Removal of the keyring pairs from the keyring pair + * dictionary is achieved using `removePair`. Retrieval of all the stored pairs via `getPairs` or perform + * lookup of a pair for a given account address or public key using `getPair`. JSON metadata associated with + * an account may be obtained using `toJson` accompanied by the account passphrase. + */ +export default class Keyring implements KeyringInstance { + private _pairs: Pairs; + private _type: KeypairType; + + constructor (options: KeyringOptions = {}) { + options.type = options.type || 'ed25519'; + + assert(options && ['ed25519', 'sr25519'].includes(options.type || 'undefined'), `Expected a keyring type of either 'ed25519' or 'sr25519', found '${options.type}`); + + this._pairs = new Pairs(); + this._type = options.type; + + setAddressPrefix(isNumber(options.addressPrefix) ? options.addressPrefix : 42); + } + + decodeAddress = decodeAddress; + encodeAddress = encodeAddress; + setAddressPrefix = setAddressPrefix; + + /** + * @description retrieve the pairs (alias for getPairs) + */ + get pairs (): Array { + return this.getPairs(); + } + + /** + * @description retrieve the publicKeys (alias for getPiblicKeys) + */ + get publicKeys (): Array { + return this.getPublicKeys(); + } + + /** + * @description Returns the type of the keyring, either ed25519 of sr25519 + */ + get type (): KeypairType { + return this._type; + } + + /** + * @name addPair + * @summary Stores an account, given a keyring pair, as a Key/Value (public key, pair) in Keyring Pair Dictionary + */ + addPair (pair: KeyringPair): KeyringPair { + return this._pairs.add(pair); + } + + /** + * @name addFromAddress + * @summary Stores an account, given an account address, as a Key/Value (public key, pair) in Keyring Pair Dictionary + * @description Allows user to explicitely provide separate inputs including account address or public key, and optionally + * the associated account metadata, and the default encoded value as arguments (that may be obtained from the json file + * of an account backup), and then generates a keyring pair from them that it passes to + * `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value. + */ + addFromAddress (address: string | Uint8Array, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null, type: KeypairType = this.type, ignoreChecksum?: boolean): KeyringPair { + const publicKey = this.decodeAddress(address, ignoreChecksum); + + return this.addPair(createPair(type, { publicKey }, meta, encoded)); + } + + /** + * @name addFromJson + * @summary Stores an account, given JSON data, as a Key/Value (public key, pair) in Keyring Pair Dictionary + * @description Allows user to provide a json object argument that contains account information (that may be obtained from the json file + * of an account backup), and then generates a keyring pair from it that it passes to + * `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value. + */ + addFromJson ({ address, encoded, encoding: { content, version }, meta }: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair { + const type = version === '0' || !Array.isArray(content) + ? this.type + : content[1]; + + return this.addFromAddress(address, meta, hexToU8a(encoded), type, ignoreChecksum); + } + + /** + * @name addFromMnemonic + * @summary Stores an account, given a mnemonic, as a Key/Value (public key, pair) in Keyring Pair Dictionary + * @description Allows user to provide a mnemonic (seed phrase that is provided when account is originally created) + * argument and a metadata argument that contains account information (that may be obtained from the json file + * of an account backup), and then generates a keyring pair from it that it passes to + * `addPair` to stores in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value. + */ + addFromMnemonic (mnemonic: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair { + return this.addFromUri(mnemonic, meta, type); + } + + /** + * @name addFromSeed + * @summary Stores an account, given seed data, as a Key/Value (public key, pair) in Keyring Pair Dictionary + * @description Stores in a keyring pair dictionary the public key of the pair as a key and the pair as the associated value. + * Allows user to provide the account seed as an argument, and then generates a keyring pair from it that it passes to + * `addPair` to store in a keyring pair dictionary the public key of the generated pair as a key and the pair as the associated value. + */ + addFromSeed (seed: Uint8Array, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair { + const keypair = type === 'sr25519' + ? schnorrkelFromSeed(seed) + : naclFromSeed(seed); + + return this.addPair(createPair(type, keypair, meta, null)); + } + + /** + * @name addFromUri + * @summary Creates an account via an suri + * @description Extracts the phrase, path and password from a SURI format for specifying secret keys `//////` (the `///password` may be omitted, and `/` and `//` maybe repeated and mixed). The secret can be a hex string, mnemonic phrase or a string (to be padded) + */ + addFromUri (suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair { + return this.addPair( + this.createFromUri(suri, meta, type) + ); + } + + /** + * @name createFromUri + * @summry Creates a Keypair from an suri + * @description This creates a pair from the suri, but does not add it to the keyring + */ + createFromUri (_suri: string, meta: KeyringPair$Meta = {}, type: KeypairType = this.type): KeyringPair { + // here we only aut-add the dev phrase if we have a hard-derived path + const suri = _suri.indexOf('//') === 0 + ? `${DEV_PHRASE}${_suri}` + : _suri; + const { password, phrase, path } = keyExtractSuri(suri); + let seed; + + if (isHex(phrase, 256)) { + seed = hexToU8a(phrase); + } else { + const str = phrase as string; + const parts = str.split(' '); + + if ([12, 15, 18, 21, 24].includes(parts.length)) { + // FIXME This keeps compat with older versions, but breaks compat with subkey + // seed = type === 'sr25519' + // ? mnemonicToMiniSecret(phrase, password) + // : mnemonicToSeed(phrase, password); + seed = mnemonicToMiniSecret(phrase, password); + } else { + assert(str.length <= 32, 'specified phrase is not a valid mnemonic and is invalid as a raw seed at > 32 bytes'); + + seed = stringToU8a(str.padEnd(32)); + } + } + + const keypair = type === 'sr25519' + ? schnorrkelFromSeed(seed) + : naclFromSeed(seed); + const derived = keyFromPath(keypair, path, type); + + return createPair(type, derived, meta, null); + } + + /** + * @name getPair + * @summary Retrieves an account keyring pair from the Keyring Pair Dictionary, given an account address + * @description Returns a keyring pair value from the keyring pair dictionary by performing + * a key lookup using the provided account address or public key (after decoding it). + */ + getPair (address: string | Uint8Array): KeyringPair { + return this._pairs.get(address); + } + + /** + * @name getPairs + * @summary Retrieves all account keyring pairs from the Keyring Pair Dictionary + * @description Returns an array list of all the keyring pair values that are stored in the keyring pair dictionary. + */ + getPairs (): Array { + return this._pairs.all(); + } + + /** + * @name getPublicKeys + * @summary Retrieves Public Keys of all Keyring Pairs stored in the Keyring Pair Dictionary + * @description Returns an array list of all the public keys associated with each of the keyring pair values that are stored in the keyring pair dictionary. + */ + getPublicKeys (): Array { + return this._pairs + .all() + .map(({ publicKey }) => + publicKey() + ); + } + + /** + * @name removePair + * @description Deletes the provided input address or public key from the stored Keyring Pair Dictionary. + */ + removePair (address: string | Uint8Array): void { + this._pairs.remove(address); + } + + /** + * @name toJson + * @summary Returns a JSON object associated with the input argument that contains metadata assocated with an account + * @description Returns a JSON object containing the metadata associated with an account + * when valid address or public key and when the account passphrase is provided if the account secret + * is not already unlocked and available in memory. Note that in [Polkadot-JS Apps](https://github.com/polkadot-js/apps) the user + * may backup their account to a JSON file that contains this information. + */ + toJson (address: string | Uint8Array, passphrase?: string): KeyringPair$Json { + return this._pairs.get(address).toJson(passphrase); + } +} + +export { Keyring, decodeAddress, encodeAddress, setAddressPrefix }; diff --git a/packages/keyring/src/pair/decode.spec.ts b/packages/keyring/src/pair/decode.spec.ts new file mode 100644 index 0000000000..43b18bca1a --- /dev/null +++ b/packages/keyring/src/pair/decode.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import testingPairs from '../testingPairs'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('decode', () => { + it('fails when no data provided', () => { + expect( + () => keyring.alice.decodePkcs8() + ).toThrow(/No encrypted data available/); + }); + + it('returns correct publicKey from encoded', () => { + const PASS = 'testing'; + + expect( + () => keyring.alice.decodePkcs8( + PASS, keyring.alice.encodePkcs8(PASS) + ) + ).not.toThrow(); + }); +}); diff --git a/packages/keyring/src/pair/decode.ts b/packages/keyring/src/pair/decode.ts new file mode 100644 index 0000000000..6378d14c24 --- /dev/null +++ b/packages/keyring/src/pair/decode.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert, stringToU8a, u8aFixLength } from '@plugnet/util'; +import { naclDecrypt } from '@plugnet/util-crypto'; +import { PairInfo } from './types'; + +import { NONCE_LENGTH, PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH, SEED_LENGTH } from './defaults'; + +const SEED_OFFSET = PKCS8_HEADER.length; + +type DecodeResult = PairInfo & { + secretKey: Uint8Array +}; + +export default function decode (passphrase?: string, _encrypted?: Uint8Array | null): DecodeResult { + assert(_encrypted, `No encrypted data available to decode`); + + const encrypted = (_encrypted as Uint8Array); + const encoded = passphrase + ? (naclDecrypt( + encrypted.subarray(NONCE_LENGTH), + encrypted.subarray(0, NONCE_LENGTH), + u8aFixLength(stringToU8a(passphrase), 256, true) + ) as Uint8Array) + : encrypted; + + assert(encoded, `Unable to unencrypt using the supplied passphrase`); + + const header = encoded.subarray(0, PKCS8_HEADER.length); + + assert(header.toString() === PKCS8_HEADER.toString(), 'Invalid Pkcs8 header found in body'); + + let secretKey = encoded.subarray(SEED_OFFSET, SEED_OFFSET + SEC_LENGTH); + let divOffset = SEED_OFFSET + SEC_LENGTH; + let divider = encoded.subarray(divOffset, divOffset + PKCS8_DIVIDER.length); + + // old-style, we have the seed here + if (divider.toString() !== PKCS8_DIVIDER.toString()) { + divOffset = SEED_OFFSET + SEED_LENGTH; + secretKey = encoded.subarray(SEED_OFFSET, divOffset); + divider = encoded.subarray(divOffset, divOffset + PKCS8_DIVIDER.length); + } + + assert(divider.toString() === PKCS8_DIVIDER.toString(), 'Invalid Pkcs8 divider found in body'); + + const pubOffset = divOffset + PKCS8_DIVIDER.length; + const publicKey = encoded.subarray(pubOffset, pubOffset + PUB_LENGTH); + + return { + publicKey, + secretKey + }; +} diff --git a/packages/keyring/src/pair/defaults.ts b/packages/keyring/src/pair/defaults.ts new file mode 100644 index 0000000000..1be12dc166 --- /dev/null +++ b/packages/keyring/src/pair/defaults.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const NONCE_LENGTH = 24; +const PKCS8_DIVIDER = new Uint8Array([161, 35, 3, 33, 0]); +const PKCS8_HEADER = new Uint8Array([48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]); +const PUB_LENGTH = 32; +const SEC_LENGTH = 64; +const SEED_LENGTH = 32; + +export { + NONCE_LENGTH, + PKCS8_DIVIDER, + PKCS8_HEADER, + PUB_LENGTH, + SEC_LENGTH, + SEED_LENGTH +}; diff --git a/packages/keyring/src/pair/encode.spec.ts b/packages/keyring/src/pair/encode.spec.ts new file mode 100644 index 0000000000..7e9befc6a6 --- /dev/null +++ b/packages/keyring/src/pair/encode.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import testingPairs from '../testingPairs'; +import { PKCS8_DIVIDER, PKCS8_HEADER, PUB_LENGTH, SEC_LENGTH } from './defaults'; + +const PKCS8_LENGTH = PKCS8_DIVIDER.length + PKCS8_HEADER.length + PUB_LENGTH + SEC_LENGTH; +const ENCODED_LENGTH = 157; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('encode', () => { + it('returns PKCS8 when no passphrase supplied', () => { + expect( + keyring.alice.encodePkcs8() + ).toHaveLength(PKCS8_LENGTH); + }); + + it('returns encoded PKCS8 when passphrase supplied', () => { + expect( + keyring.alice.encodePkcs8('testing') + ).toHaveLength(ENCODED_LENGTH); + }); +}); diff --git a/packages/keyring/src/pair/encode.ts b/packages/keyring/src/pair/encode.ts new file mode 100644 index 0000000000..e73a4afe8b --- /dev/null +++ b/packages/keyring/src/pair/encode.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { PairInfo } from './types'; + +import { stringToU8a, u8aConcat, u8aFixLength, assert } from '@plugnet/util'; +import { naclEncrypt } from '@plugnet/util-crypto'; + +import { PKCS8_DIVIDER, PKCS8_HEADER } from './defaults'; + +export default function encode ({ publicKey, secretKey }: PairInfo, passphrase?: string): Uint8Array { + assert(secretKey, 'Expected a valid secretKey to be passed to encode'); + + const encoded = u8aConcat( + PKCS8_HEADER, + secretKey as Uint8Array, + PKCS8_DIVIDER, + publicKey + ); + + if (!passphrase) { + return encoded; + } + + const { encrypted, nonce } = naclEncrypt(encoded, u8aFixLength(stringToU8a(passphrase), 256, true)); + + return u8aConcat(nonce, encrypted); +} diff --git a/packages/keyring/src/pair/index.spec.ts b/packages/keyring/src/pair/index.spec.ts new file mode 100644 index 0000000000..cdf2f0d6c3 --- /dev/null +++ b/packages/keyring/src/pair/index.spec.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import testingPairs from '../testingPairs'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('pair', () => { + const SIGNATURE = new Uint8Array([80, 191, 198, 147, 225, 207, 75, 88, 126, 39, 129, 109, 191, 38, 72, 181, 75, 254, 81, 143, 244, 79, 237, 38, 236, 141, 28, 252, 134, 26, 169, 234, 79, 33, 153, 158, 151, 34, 175, 188, 235, 20, 35, 135, 83, 120, 139, 211, 233, 130, 1, 208, 201, 215, 73, 80, 56, 98, 185, 196, 11, 8, 193, 14]); + + it('has a publicKey', () => { + expect( + keyring.alice.publicKey() + ).toEqual( + new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]) + ); + }); + + it('allows signing', () => { + expect( + keyring.alice.sign( + new Uint8Array([0x61, 0x62, 0x63, 0x64]) + ) + ).toEqual(SIGNATURE); + }); + + it('validates a correctly signed message', () => { + expect( + keyring.alice.verify( + new Uint8Array([0x61, 0x62, 0x63, 0x64]), + SIGNATURE + ) + ).toEqual(true); + }); + + it('fails a correctly signed message (message changed)', () => { + expect( + keyring.alice.verify( + new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]), + SIGNATURE + ) + ).toEqual(false); + }); + + it('allows setting/getting of meta', () => { + keyring.bob.setMeta({ foo: 'bar', something: 'else' }); + + expect(keyring.bob.getMeta()).toMatchObject({ foo: 'bar', something: 'else' }); + + keyring.bob.setMeta({ something: 'thing' }); + + expect(keyring.bob.getMeta()).toMatchObject({ foo: 'bar', something: 'thing' }); + }); +}); diff --git a/packages/keyring/src/pair/index.ts b/packages/keyring/src/pair/index.ts new file mode 100644 index 0000000000..1c58460d05 --- /dev/null +++ b/packages/keyring/src/pair/index.ts @@ -0,0 +1,104 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair, KeypairType } from '@plugnet/util-crypto/types'; +import { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '../types'; +import { PairInfo } from './types'; + +import { naclKeypairFromSeed as naclFromSeed, naclSign, naclVerify, schnorrkelKeypairFromSeed as schnorrkelFromSeed, schnorrkelSign, schnorrkelVerify } from '@plugnet/util-crypto'; + +import { encodeAddress } from '../address'; +import decode from './decode'; +import encode from './encode'; +import toJson from './toJson'; + +const isSr25519 = (type: KeypairType): boolean => + type === 'sr25519'; + +const fromSeed = (type: KeypairType, seed: Uint8Array) => + isSr25519(type) + ? schnorrkelFromSeed(seed) + : naclFromSeed(seed); + +const sign = (type: KeypairType, message: Uint8Array, pair: Partial) => + isSr25519(type) + ? schnorrkelSign(message, pair) + : naclSign(message, pair); + +const verify = (type: KeypairType, message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array) => + isSr25519(type) + ? schnorrkelVerify(message, signature, publicKey) + : naclVerify(message, signature, publicKey); + +/** + * @name pair + * @summary Creates a keyring pair object + * @description Creates a keyring pair object with provided account public key, metadata, and encoded arguments. + * The keyring pair stores the account state including the encoded address and associated metadata. + * + * It has properties whose values are functions that may be called to perform account actions: + * + * - `address` function retrieves the address associated with the account. + * - `decodedPkcs8` function is called with the account passphrase and account encoded public key. + * It decodes the encoded public key using the passphrase provided to obtain the decoded account public key + * and associated secret key that are then available in memory, and changes the account address stored in the + * state of the pair to correspond to the address of the decoded public key. + * - `encodePkcs8` function when provided with the correct passphrase associated with the account pair + * and when the secret key is in memory (when the account pair is not locked) it returns an encoded + * public key of the account. + * - `getMeta` returns the metadata that is stored in the state of the pair, either when it was originally + * created or set via `setMeta`. + * - `publicKey` returns the public key stored in memory for the pair. + * - `sign` may be used to return a signature by signing a provided message with the secret + * key (if it is in memory) using Nacl. + * - `toJson` calls another `toJson` function and provides the state of the pair, + * it generates arguments to be passed to the other `toJson` function including an encoded public key of the account + * that it generates using the secret key from memory (if it has been made available in memory) + * and the optionally provided passphrase argument. It passes a third boolean argument to `toJson` + * indicating whether the public key has been encoded or not (if a passphrase argument was provided then it is encoded). + * The `toJson` function that it calls returns a JSON object with properties including the `address` + * and `meta` that are assigned with the values stored in the corresponding state variables of the account pair, + * an `encoded` property that is assigned with the encoded public key in hex format, and an `encoding` + * property that indicates whether the public key value of the `encoded` property is encoded or not. + */ +export default function createPair (type: KeypairType, { publicKey, secretKey }: PairInfo, meta: KeyringPair$Meta = {}, encoded: Uint8Array | null = null): KeyringPair { + return { + type, + address: (): string => + encodeAddress(publicKey), + decodePkcs8: (passphrase?: string, _encoded?: Uint8Array | null): void => { + const decoded = decode(passphrase, _encoded || encoded); + + if (decoded.secretKey.length === 64) { + publicKey = decoded.publicKey; + secretKey = decoded.secretKey; + } else { + const pair = fromSeed(type, decoded.secretKey); + + publicKey = pair.publicKey; + secretKey = pair.secretKey; + } + }, + encodePkcs8: (passphrase?: string): Uint8Array => + encode({ publicKey, secretKey }, passphrase), + getMeta: (): KeyringPair$Meta => + meta, + isLocked: (): boolean => + (!secretKey || secretKey.length === 0), + lock: (): void => { + secretKey = new Uint8Array(0); + }, + publicKey: (): Uint8Array => + publicKey, + setMeta: (additional: KeyringPair$Meta): void => { + meta = { ...meta, ...additional }; + }, + sign: (message: Uint8Array): Uint8Array => + sign(type, message, { publicKey, secretKey }), + toJson: (passphrase?: string): KeyringPair$Json => + toJson(type, { meta, publicKey }, encode({ publicKey, secretKey }, passphrase), !!passphrase), + verify: (message: Uint8Array, signature: Uint8Array): boolean => + verify(type, message, signature, publicKey) + }; +} diff --git a/packages/keyring/src/pair/nobody.ts b/packages/keyring/src/pair/nobody.ts new file mode 100644 index 0000000000..85e77e75d1 --- /dev/null +++ b/packages/keyring/src/pair/nobody.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair, KeyringPair$Json, KeyringPair$Meta } from '../types'; + +import addressEncode from '../address/encode'; + +const publicKey = new Uint8Array(32); +const address = addressEncode(publicKey); +const meta = { + isTesting: true, + name: 'nobody' +}; +const json: KeyringPair$Json = { + address, + encoded: '', + encoding: { + content: ['pkcs8', 'ed25519'], + type: 'none', + version: '0' + }, + meta +}; + +export default function everybody (): KeyringPair { + return { + type: 'ed25519', + address: (): string => + address, + decodePkcs8: (passphrase?: string, encoded?: Uint8Array): void => + undefined, + encodePkcs8: (passphrase?: string): Uint8Array => + new Uint8Array(0), + getMeta: (): KeyringPair$Meta => + meta, + isLocked: (): boolean => + true, + lock: (): void => { + // no locking, it is always locked + }, + publicKey: (): Uint8Array => + publicKey, + setMeta: (meta: KeyringPair$Meta): void => + undefined, + sign: (message: Uint8Array): Uint8Array => + new Uint8Array(64), + toJson: (passphrase?: string): KeyringPair$Json => + json, + verify: (message: Uint8Array, signature: Uint8Array): boolean => + false + }; +} diff --git a/packages/keyring/src/pair/toJson.spec.ts b/packages/keyring/src/pair/toJson.spec.ts new file mode 100644 index 0000000000..022055ec6b --- /dev/null +++ b/packages/keyring/src/pair/toJson.spec.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import testingPairs from '../testingPairs'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('toJson', () => { + it('creates an unencoded output with no passphrase', () => { + expect( + keyring.alice.toJson() + ).toMatchObject({ + address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua', + encoded: '0x3053020101300506032b657004220420416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4fa123032100d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f', + encoding: { + content: ['pkcs8', 'ed25519'], + type: 'none', + version: '2' + }, + meta: { + name: 'alice', + isTesting: true + } + }); + }); + + it('creates an encoded output with passphrase', () => { + const json = keyring.alice.toJson('testing'); + + expect(json.encoded).toHaveLength(316); + expect(json).toMatchObject({ + address: '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua', + encoding: { + content: ['pkcs8', 'ed25519'], + type: 'xsalsa20-poly1305', + version: '2' + } + }); + }); +}); diff --git a/packages/keyring/src/pair/toJson.ts b/packages/keyring/src/pair/toJson.ts new file mode 100644 index 0000000000..0037168e32 --- /dev/null +++ b/packages/keyring/src/pair/toJson.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeypairType } from '@plugnet/util-crypto/types'; +import { KeyringPair$Json, KeyringPair$Meta } from '../types'; + +import { u8aToHex } from '@plugnet/util'; + +import { encodeAddress } from '../address'; + +type PairStateJson = KeyringPair$Meta & { + publicKey: Uint8Array +}; + +export default function toJson (type: KeypairType, { publicKey, meta }: PairStateJson, encoded: Uint8Array, isEncrypted: boolean): KeyringPair$Json { + return { + address: encodeAddress(publicKey), + encoded: u8aToHex(encoded), + encoding: { + content: ['pkcs8', type], + type: isEncrypted + ? 'xsalsa20-poly1305' + : 'none', + version: '2' + }, + meta + }; +} diff --git a/packages/keyring/src/pair/types.ts b/packages/keyring/src/pair/types.ts new file mode 100644 index 0000000000..23dee4b6fc --- /dev/null +++ b/packages/keyring/src/pair/types.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type PairInfo = { + publicKey: Uint8Array, + secretKey?: Uint8Array, + seed?: Uint8Array | null +}; diff --git a/packages/keyring/src/pairs.ts b/packages/keyring/src/pairs.ts new file mode 100644 index 0000000000..408ef3b50b --- /dev/null +++ b/packages/keyring/src/pairs.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPairs, KeyringPair } from './types'; + +import { assert, isHex, isU8a, u8aToHex, u8aToU8a } from '@plugnet/util'; + +import addressDecode from './address/decode'; + +type KeyringPairMap = { + // @ts-ignore we use coercion :( + [index: Uint8Array]: KeyringPair +}; + +export default class Pairs implements KeyringPairs { + private _map: KeyringPairMap; + + constructor () { + this._map = {}; + } + + add (pair: KeyringPair): KeyringPair { + // @ts-ignore we use coercion :( + this._map[pair.publicKey()] = pair; + + return pair; + } + + all (): Array { + return Object.values(this._map); + } + + get (address: string | Uint8Array): KeyringPair { + // @ts-ignore we use coercion :( + const pair = this._map[addressDecode(address)]; + + assert(pair, () => { + const formatted: string = isU8a(address) || isHex(address) + ? u8aToHex(u8aToU8a(address)) + : address; + + return `Unable to retrieve keypair '${formatted}'`; + }); + + return pair; + } + + remove (address: string | Uint8Array): void { + // @ts-ignore we use coercion :( + delete this._map[addressDecode(address)]; + } +} diff --git a/packages/keyring/src/suri.spec.ts b/packages/keyring/src/suri.spec.ts new file mode 100644 index 0000000000..b056745542 --- /dev/null +++ b/packages/keyring/src/suri.spec.ts @@ -0,0 +1,77 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// From https://github.com/paritytech/substrate/wiki/Secret-URI-Test-Vectors + +import { u8aToHex } from '@plugnet/util'; +import { cryptoWaitReady } from '@plugnet/util-crypto'; + +import Keyring from '.'; + +const PHRASE = 'bottom drive obey lake curtain smoke basket hold race lonely fit walk'; + +const TESTS = [ + { + pk: '0x46ebddef8cd9bb167dc30878d7113b7e168e6f0646beffd77d69d39bad76b47a', + ss: '5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV', + uri: PHRASE + }, + { + pk: '0xb69355deefa7a8f33e9297f5af22e680f03597a99d4f4b1c44be47e7a2275802', + ss: '5GC6LfpV352HtJPySfAecb5JdePtf4R9Vq49NUU8RhzgBqgq', + uri: `${PHRASE}///password` + }, + { + pk: '0x40b9675df90efa6069ff623b0fdfcf706cd47ca7452a5056c7ad58194d23440a', + ss: '5DXZzrDxHbkQov4QBAY4TjpwnHCMrKXkomTnKSw8UArBEY5v', + uri: `${PHRASE}/foo` + }, + { + pk: '0x547d4a55642ec7ebadc0bd29b6e570b8c926059b3c0655d4948075e9a7e6f31e', + ss: '5DyV6fZuvPemWrUqBgWwTSgoV86w6xms3KhkFU6cQcWxU8eP', + uri: `${PHRASE}//foo` + }, + { + pk: '0x3841947ffcde6f5fef26fb68b59bb8665637e30e32ec2051f99cf6b9c674fe09', + ss: '5DLU27is5iViNopQb2KxsTyPx6j4vCu8X3sk3j3NNLkPCqKM', + uri: `${PHRASE}//foo/bar` + }, + { + pk: '0xdc142f7476a7b0aa262aeccf207f1d18daa90762db393006741e8a31f39dbc53', + ss: '5H3GPTqDSpjkfDwbHy12PD6BWm8jvGSX4xYC8UMprHpTPcRg', + uri: `${PHRASE}/foo//bar` + }, + { + pk: '0xa2e56b06407a6d1e819d2fc33fa0ec604b29c2e868b70b3696bb049b8725934b', + ss: '5FkHmNgbg64MwStgCyDi2Uw3ufFu11mqQgmWT9uwK4Lghvpv', + uri: `${PHRASE}//foo/bar//42/69` + }, + { + pk: '0x0e0d24e3e1ff2c07f269c99e2e0df8681fda1851ac42fc846ca2daaa90cd8f14', + ss: '5CP8S23JBNXYNpJsL7ESPJBNnUZE6itcfM4EnDxEhaVEU6dT', + uri: `${PHRASE}//foo/bar//42/69///password` + }, + { + pk: '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d', + ss: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', + uri: `${PHRASE}//Alice` + } +]; + +describe('keyring.addFromUri', () => { + const keyring = new Keyring({ type: 'sr25519' }); + + beforeEach(async () => { + await cryptoWaitReady(); + }); + + TESTS.forEach(({ pk, ss, uri }) => { + it(`creates ${uri}`, () => { + const pair = keyring.addFromUri(uri); + + expect(u8aToHex(pair.publicKey())).toEqual(pk); + expect(pair.address()).toEqual(ss); + }); + }); +}); diff --git a/packages/keyring/src/testing.ts b/packages/keyring/src/testing.ts new file mode 100644 index 0000000000..ca5dd7791f --- /dev/null +++ b/packages/keyring/src/testing.ts @@ -0,0 +1,74 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringInstance, KeyringOptions } from './types'; + +import { hexToU8a } from '@plugnet/util'; + +import createPair from './pair'; +import Keyring from '.'; + +// As per substrate +const SEEDS = ['Alice', 'Alice//stash', 'Bob', 'Charlie', 'Dave', 'Eve', 'Ferdie']; +// NOTE This is not great, but a testing keyring is for testing - what happens is that in most cases +// the keyring is initialises before anythign else. Since the sr25519 crypto is async, this creates +// problems with adding the keys +const PAIRS = [ + [ + hexToU8a('0x98319d4ff8a9508c4bb0cf0b5a78d760a0b2082c02775e6e82370816fedfff48925a225d97aa00682d6a59b95b18780c10d7032336e88f3442b42361f4a66011'), + hexToU8a('0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d') + ], + [ + hexToU8a('0xe8da6c9d810e020f5e3c7f5af2dea314cbeaa0d72bc6421e92c0808a0c584a6046ab28e97c3ffc77fe12b5a4d37e8cd4afbfebbf2391ffc7cb07c0f38c023efd'), + hexToU8a('0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f') + ], + [ + hexToU8a('0x081ff694633e255136bdb456c20a5fc8fed21f8b964c11bb17ff534ce80ebd5941ae88f85d0c1bfc37be41c904e1dfc01de8c8067b0d6d5df25dd1ac0894a325'), + hexToU8a('0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48') + ], + [ + hexToU8a('0xa8f2d83016052e5d6d77b2f6fd5d59418922a09024cda701b3c34369ec43a7668faf12ff39cd4e5d92bb773972f41a7a5279ebc2ed92264bed8f47d344f8f18c'), + hexToU8a('0x90b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22') + ], + [ + hexToU8a('0x20e05482ca4677e0edbc58ae9a3a59f6ed3b1a9484ba17e64d6fe8688b2b7b5d108c4487b9323b98b11fe36cb301b084e920f7b7895536809a6d62a451b25568'), + hexToU8a('0x306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20') + ], + [ + hexToU8a('0x683576abfd5dc35273e4264c23095a1bf21c14517bece57c7f0cc5c0ed4ce06a3dbf386b7828f348abe15d76973a72009e6ef86a5c91db2990cb36bb657c6587'), + hexToU8a('0xe659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e') + ], + [ + hexToU8a('0xb835c20f450079cf4f513900ae9faf8df06ad86c681884122c752a4b2bf74d4303e4f21bc6cc62bb4eeed5a9cce642c25e2d2ac1464093b50f6196d78e3a7426'), + hexToU8a('0x1cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c') + ] +]; + +/** + * @name testKeyring + * @summary Create an instance of Keyring pre-populated with locked test accounts + * @description The test accounts (i.e. alice, bob, dave, eve, ferdie) + * are available on the dev chain and each test account is initialised with DOT funds. + */ +export default function testKeyring (options: KeyringOptions = {}, isDerived: boolean = true): KeyringInstance { + const keyring = new Keyring(options); + + SEEDS.forEach((entry, index) => { + const meta = { + isTesting: true, + name: entry.replace('//', '_').toLowerCase() + }; + const pair = !isDerived + ? keyring.addFromUri(entry, meta, options.type) + : keyring.addPair( + createPair('sr25519', { publicKey: PAIRS[index][1], secretKey: PAIRS[index][0] }, meta) + ); + + pair.lock = () => { + // we don't have lock/unlock functionality here + }; + }); + + return keyring; +} diff --git a/packages/keyring/src/testingPairs.spec.ts b/packages/keyring/src/testingPairs.spec.ts new file mode 100644 index 0000000000..e386456c78 --- /dev/null +++ b/packages/keyring/src/testingPairs.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { cryptoWaitReady } from '@plugnet/util-crypto'; + +import testingPairs from './testingPairs'; + +describe('testingPairs', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + it('creates without failing', () => { + expect( + Object.keys(testingPairs()) + ).toHaveLength(1 + 7); + }); + + it('has the correct address for Alice (non-HDKD)', () => { + expect( + testingPairs({ type: 'ed25519' }, false).alice.address() + ).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua'); + }); + + it('has the correct address for Alice (HDKD)', () => { + expect( + testingPairs({ type: 'ed25519' }, true).alice.address() + ).toEqual('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'); + }); +}); diff --git a/packages/keyring/src/testingPairs.ts b/packages/keyring/src/testingPairs.ts new file mode 100644 index 0000000000..a5c20b0979 --- /dev/null +++ b/packages/keyring/src/testingPairs.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair, KeyringOptions } from './types'; + +import createKeyring from './testing'; +import nobody from './pair/nobody'; + +type TestKeyringMap = { + [index: string]: KeyringPair +}; + +export default function testKeyringPairs (options?: KeyringOptions, isDerived: boolean = true): TestKeyringMap { + const keyring = createKeyring(options, isDerived); + const pairs = keyring.getPairs(); + + return pairs.reduce((result, pair) => { + const { name } = pair.getMeta(); + + result[name as string] = pair; + + return result; + }, { 'nobody': nobody() } as TestKeyringMap); +} diff --git a/packages/keyring/src/types.ts b/packages/keyring/src/types.ts new file mode 100644 index 0000000000..96480db3c0 --- /dev/null +++ b/packages/keyring/src/types.ts @@ -0,0 +1,76 @@ +// Copyright 2017-2019 @polkadot/keyring authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeypairType } from '@plugnet/util-crypto/types'; +import { Prefix } from './address/types'; + +export type KeyringOptions = { + addressPrefix?: Prefix, + type?: KeypairType +}; + +export type KeyringPair$Meta = { + [index: string]: any +}; + +export type KeyringPair$JsonVersion = '0' | '1' | '2'; + +export type KeyringPair$JsonEncoding = { + content: ['pkcs8', KeypairType], + type: 'xsalsa20-poly1305' | 'none', + version: KeyringPair$JsonVersion +}; + +export type KeyringPair$Json = { + address: string, + encoded: string, + encoding: KeyringPair$JsonEncoding, + meta: KeyringPair$Meta +}; + +export interface KeyringPair { + readonly type: KeypairType; + + address: () => string; + decodePkcs8: (passphrase?: string, encoded?: Uint8Array) => void; + encodePkcs8: (passphrase?: string) => Uint8Array; + getMeta: () => KeyringPair$Meta; + isLocked: () => boolean; + lock: () => void; + publicKey: () => Uint8Array; + setMeta: (meta: KeyringPair$Meta) => void; + sign (message: Uint8Array): Uint8Array; + toJson (passphrase?: string): KeyringPair$Json; + verify (message: Uint8Array, signature: Uint8Array): boolean; +} + +export interface KeyringPairs { + add: (pair: KeyringPair) => KeyringPair; + all: () => Array; + get: (address: string | Uint8Array) => KeyringPair; + remove: (address: string | Uint8Array) => void; +} + +export interface KeyringInstance { + readonly pairs: Array; + readonly publicKeys: Array; + readonly type: KeypairType; + + decodeAddress (encoded: string | Uint8Array, ignoreChecksum?: boolean): Uint8Array; + encodeAddress (key: Uint8Array | string): string; + setAddressPrefix (prefix: Prefix): void; + + addPair (pair: KeyringPair): KeyringPair; + addFromAddress (address: string | Uint8Array, meta?: KeyringPair$Meta, encoded?: Uint8Array | null, type?: KeypairType, ignoreChecksum?: boolean): KeyringPair; + addFromJson (pair: KeyringPair$Json, ignoreChecksum?: boolean): KeyringPair; + addFromMnemonic (mnemonic: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair; + addFromSeed (seed: Uint8Array, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair; + addFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair; + createFromUri (suri: string, meta?: KeyringPair$Meta, type?: KeypairType): KeyringPair; + getPair (address: string | Uint8Array): KeyringPair; + getPairs (): Array; + getPublicKeys (): Array; + removePair (address: string | Uint8Array): void; + toJson (address: string | Uint8Array, passphrase?: string): KeyringPair$Json; +} diff --git a/packages/rpc-core/LICENSE b/packages/rpc-core/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/rpc-core/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/rpc-core/README.md b/packages/rpc-core/README.md new file mode 100644 index 0000000000..3747fe7ad4 --- /dev/null +++ b/packages/rpc-core/README.md @@ -0,0 +1,71 @@ +# @polkadot/rpc-core + +This library provides a clean wrapper around all the methods exposed by a Polkadot network client. + +## Usage + +Installation - + +``` +yarn add @polkadot/rpc-core +``` + +Initialisation - + +```js +import Rpc from '@polkadot/rpc-core'; +import WsProvider from '@polkadot/rpc-provider/ws'; + +const provider = new WsProvider('http://127.0.0.1:9944'); +const api = new Rpc(provider); +``` + +Retrieving the block header object for a given block header hash (a 0x-prefixed hex string with length of 64) - + +```js +api.chain + .getHeader('0x1234567890') + .then((header) => console.log(header)) + .catch((error) => console.error(error)); +``` + +Retrieving the best block number, parent hash, state root hash, extrinsics root hash, and digest (once-off) - + +```js +api.chain + .getHead() + .then((headerHash) => { + return api.chain.getHeader(headerHash); + }) + .then((header) => { + console.log(`best #${header.blockNumber.toString()}`); + console.log(`parentHash: ${header.parentHash.toString()}`); + console.log(`stateRoot: ${header.stateRoot.toString()}`); + console.log(`extrinsicsRoot: ${header.extrinsicsRoot.toString()}`); + console.log(`digest: ${header.digest.toString()}`); + }) + .catch((error) => { + console.error('error:', error); + }); +``` + +Retrieving best header via subscription - + +```js +api.chain + .subscribeNewHead((header) => { + console.log(`best #${header.blockNumber}`); + }) + .then((subscriptionId) => { + console.log(`subscriptionId: ${subscriptionId}`); + // id for the subscription, can unsubscribe via + // api.chain.subscribeNewHead.unsubscribe(subscriptionId); + }) + .catch((error) => { + console.error('error subscribing:', error); + }); +``` + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/rpc-core/package.json b/packages/rpc-core/package.json new file mode 100644 index 0000000000..ae4f943583 --- /dev/null +++ b/packages/rpc-core/package.json @@ -0,0 +1,38 @@ +{ + "name": "@plugnet/rpc-core", + "version": "0.76.1", + "description": "A JavaScript wrapper for the Polkadot JsonRPC interface", + "main": "index.js", + "keywords": [ + "Polkadot", + "JsonRPC" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-core#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/jsonrpc": "^0.76.1", + "@plugnet/rpc-provider": "^0.76.1", + "@plugnet/types": "^0.76.1", + "@plugnet/util": "^0.76.1" + } +} diff --git a/packages/rpc-core/src/formatting.spec.ts b/packages/rpc-core/src/formatting.spec.ts new file mode 100644 index 0000000000..095d9e35f1 --- /dev/null +++ b/packages/rpc-core/src/formatting.spec.ts @@ -0,0 +1,80 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import storage from '@plugnet/storage/static'; + +import Api from '.'; + +const ADDR_ONE = '5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'; +const ADDR_TWO = '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'; + +const ENC_ONE = '0x4af2c53fce3ec33c6ccccf22e926f1a7'; +const ENC_TWO = '0x3e62f7ed6e788e1337bce2a97b68a12a'; + +describe('formatting', () => { + let api: Api; + let provider: any; + + beforeEach(() => { + provider = { + send: jest.fn((method, params) => + Promise.resolve('0x0102') + ), + subscribe: jest.fn((type, method, params, subscription) => + subscription(null, { + block: '0x1234', + changes: [ + [ENC_ONE, '0x0102'], + [ENC_TWO, '0x0201'] + ] + }) + ) + }; + api = new Api(provider); + }); + + it('encodes key (with params), decoding response', () => { + return api.state + .getStorage( + [storage.balances.freeBalance, ADDR_ONE] + ) + .then((value) => { + expect( + provider.send + ).toHaveBeenCalledWith( + 'state_getStorage', + [ENC_ONE] + ); + expect(value.toNumber()).toEqual(513); + }); + }); + + it('encodes multiple keys, decoding multiple results', (done) => { + return api.state.subscribeStorage( + [ + [storage.balances.freeBalance, ADDR_ONE], + [storage.balances.freeBalance, ADDR_TWO] + ], + (value: any) => { + console.error(value); + + expect( + provider.subscribe + ).toHaveBeenCalledWith( + 'state_storage', + 'state_subscribeStorage', + [[ENC_ONE, ENC_TWO]], + expect.anything() + ); + expect( + value.map((balance: BN) => + balance.toNumber() + ) + ).toEqual([513, 258]); + + done(); + }); + }); +}); diff --git a/packages/rpc-core/src/index.spec.ts b/packages/rpc-core/src/index.spec.ts new file mode 100644 index 0000000000..60e8dcad22 --- /dev/null +++ b/packages/rpc-core/src/index.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Api from '.'; + +describe('Api', () => { + let api: Api; + let provider: any; + let sendSpy: any; + + beforeEach(() => { + provider = { + send: (method: any, params: Array) => { + return Promise.resolve(params[0]); + } + }; + sendSpy = jest.spyOn(provider, 'send'); + api = new Api(provider); + }); + + afterEach(() => { + sendSpy.mockRestore(); + }); + + it('requires a provider with a send method', () => { + expect( + () => new Api({} as any) + ).toThrow(/Expected Provider/); + }); + + it('sets up the chain interface', () => { + expect(api.chain).toBeDefined(); + }); + + it('sets up the state interface', () => { + expect(api.state).toBeDefined(); + }); +}); diff --git a/packages/rpc-core/src/index.ts b/packages/rpc-core/src/index.ts new file mode 100644 index 0000000000..e7e96702bd --- /dev/null +++ b/packages/rpc-core/src/index.ts @@ -0,0 +1,250 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface, ProviderInterface$Callback } from '@plugnet/rpc-provider/types'; +import { RpcSection, RpcMethod } from '@plugnet/jsonrpc/types'; +import { RpcInterface, RpcInterface$Method, RpcInterface$Section } from './types'; + +import interfaces from '@plugnet/jsonrpc'; +import { WsProvider } from '@plugnet/rpc-provider'; +import { Codec } from '@plugnet/types/types'; +import { Option, StorageChangeSet, StorageKey, Vector, createClass, createType } from '@plugnet/types'; +import { ExtError, assert, isFunction, isNull, logger } from '@plugnet/util'; + +const l = logger('rpc-core'); + +/** + * @name Rpc + * @summary The API may use a HTTP or WebSockets provider. + * @description It allows for querying a Polkadot Client Node. + * WebSockets provider is recommended since HTTP provider only supports basic querying. + * + * ```mermaid + * graph LR; + * A[Api] --> |WebSockets| B[WsProvider]; + * B --> |endpoint| C[ws://127.0.0.1:9944] + * ``` + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/rpc-core'; + * import WsProvider from '@plugnet/rpc-provider/ws'; + * + * const provider = new WsProvider('ws://127.0.0.1:9944'); + * const api = new Api(provider); + * ``` + */ +export default class Rpc implements RpcInterface { + readonly _provider: ProviderInterface; + readonly author: RpcInterface$Section; + readonly chain: RpcInterface$Section; + readonly state: RpcInterface$Section; + readonly system: RpcInterface$Section; + + /** + * @constructor + * Default constructor for the Api Object + * @param {ProviderInterface} provider An API provider using HTTP or WebSocket + */ + constructor (provider: ProviderInterface = new WsProvider()) { + assert(provider && isFunction(provider.send), 'Expected Provider to API create'); + + this._provider = provider; + + this.author = this.createInterface(interfaces.author); + this.chain = this.createInterface(interfaces.chain); + this.state = this.createInterface(interfaces.state); + this.system = this.createInterface(interfaces.system); + } + + /** + * @name signature + * @summary Returns a string representation of the method with inputs and outputs. + * @description + * Formats the name, inputs and outputs into a human-readable string. This contains the input parameter names input types and output type. + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/rpc-core'; + * + * Api.signature({ name: 'test_method', params: [ { name: 'dest', type: 'Address' } ], type: 'Address' }); // => test_method (dest: Address): Address + * ``` + */ + static signature ({ method, params, type }: RpcMethod): string { + const inputs = params.map(({ name, type }) => + `${name}: ${type}` + ).join(', '); + + return `${method} (${inputs}): ${type}`; + } + + /** + * @description Manually disconnect from the attached provider + */ + disconnect (): void { + this._provider.disconnect(); + } + + private createInterface ({ methods }: RpcSection): RpcInterface$Section { + return Object + .keys(methods) + .reduce((exposed, method) => { + + const def = methods[method]; + + exposed[method] = def.isSubscription + ? this.createMethodSubscribe(def) + : this.createMethodSend(def); + + return exposed; + }, {} as RpcInterface$Section); + } + + private createMethodSend (method: RpcMethod): RpcInterface$Method { + const rpcName = `${method.section}_${method.method}`; + + const call = async (...values: Array): Promise => { + // TODO Warn on deprecated methods + try { + const params = this.formatInputs(method, values); + const paramsJson = params.map((param) => param.toJSON()); + const result = await this._provider.send(rpcName, paramsJson); + + return this.formatOutput(method, params, result); + } catch (error) { + const message = `${Rpc.signature(method)}:: ${error.message}`; + + l.error(message); + + throw new ExtError(message, (error as ExtError).code, undefined); + } + }; + + return call as RpcInterface$Method; + } + + private createMethodSubscribe (method: RpcMethod): RpcInterface$Method { + const [updateType, subMethod, unsubMethod] = method.pubsub; + const subName = `${method.section}_${subMethod}`; + const unsubName = `${method.section}_${unsubMethod}`; + const subType = `${method.section}_${updateType}`; + + const unsubscribe = (subscriptionId: any): Promise => + this._provider.unsubscribe(subType, unsubName, subscriptionId); + const _call = async (...values: Array): Promise => { + try { + const cb: ProviderInterface$Callback = values.pop(); + + assert(isFunction(cb), `Expected callback in last position of params`); + + const params = this.formatInputs(method, values); + const paramsJson = params.map((param) => param.toJSON()); + const update = (error: Error | null, result?: any) => { + if (error) { + l.error(`${Rpc.signature(method)}:: ${error.message}`); + return; + } + + cb(this.formatOutput(method, params, result)); + }; + + return this._provider.subscribe(subType, subName, paramsJson, update); + } catch (error) { + const message = `${Rpc.signature(method)}:: ${error.message}`; + + l.error(message); + + throw new ExtError(message, (error as ExtError).code, undefined); + } + }; + + const call = _call as RpcInterface$Method; + + call.unsubscribe = unsubscribe; + + return call; + } + + private formatInputs (method: RpcMethod, inputs: Array): Array { + const reqArgCount = method.params.filter(({ isOptional }) => !isOptional).length; + const optText = reqArgCount === method.params.length + ? '' + : ` (${method.params.length - reqArgCount} optional)`; + + assert(inputs.length >= reqArgCount && inputs.length <= method.params.length, `Expected ${method.params.length} parameters${optText}, ${inputs.length} found instead`); + + return inputs.map((input, index) => + createType(method.params[index].type, input) + ); + } + + private formatOutput (method: RpcMethod, params: Array, result?: any): Codec | Array { + const base = createType(method.type as string, result); + + if (method.type === 'StorageData') { + // single return value (via state.getStorage), decode the value based on the + // outputType that we have specified. Fallback to Data on nothing + const key = params[0] as StorageKey; + const type = key.outputType || 'Data'; + const Clazz = createClass(type); + const meta = key.meta || { default: undefined, modifier: { isOptional: true } }; + + if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) { + // linked map + return new Clazz(base); + } else { + return meta.modifier.isOptional + ? new Option(Clazz, isNull(result) ? null : new Clazz(base)) + : new Clazz(base); + } + } else if (method.type === 'StorageChangeSet') { + // multiple return values (via state.storage subscription), decode the values + // one at a time, all based on the query types. Three values can be returned - + // - Base - There is a valid value, non-empty + // - null - The storage key is empty (but in the resultset) + // - undefined - The storage value is not in the resultset + return (params[0] as Vector).reduce((result, key: StorageKey) => { + // Fallback to Data (i.e. just the encoding) if we don't have a specific type + const type = key.outputType || 'Data'; + const Clazz = createClass(type); + + // see if we have a result value for this specific key + const hexKey = key.toHex(); + const { value } = (base as StorageChangeSet).changes.find((item) => + item.key.toHex() === hexKey + ) || { value: null }; + const meta = key.meta || { default: undefined, modifier: { isOptional: true } }; + + if (!value) { + // if we don't have a value, do not fill in the entry, it will be up to the + // caller to sort this out, either ignoring or having a cache for older values + result.push(undefined); + } else { + if (key.meta && key.meta.type.isMap && key.meta.type.asMap.isLinked) { + // linked map + result.push(new Clazz(value.unwrapOr(null))); + } else { + if (meta.modifier.isOptional) { + // create option either with the existing value, or empty when + // there is no value returned + result.push(new Option(Clazz, value.isNone ? null : new Clazz(value.unwrap()))); + } else { + // for `null` we fallback to the default value, or create an empty type, + // otherwise we return the actual value as retrieved + result.push(new Clazz(value.unwrapOr(meta.default))); + } + } + } + + return result; + }, [] as Array); + } + + return base; + } +} diff --git a/packages/rpc-core/src/methodSend.spec.ts b/packages/rpc-core/src/methodSend.spec.ts new file mode 100644 index 0000000000..a7daaa707c --- /dev/null +++ b/packages/rpc-core/src/methodSend.spec.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Api from '.'; + +describe('methodSend', () => { + let api: Api; + let methods: any; + let provider: any; + + beforeEach(() => { + methods = { + blah: { + method: 'blah', + params: [ + { name: 'foo', type: 'Bytes' } + ], + section: 'test', + type: 'Bytes' + }, + bleh: { + method: 'bleh', + params: [], + section: 'test', + type: 'Bytes' + } + }; + + provider = { + send: jest.fn((method, params) => { + return Promise.resolve(params[0]); + }) + }; + + api = new Api(provider); + }); + + it('wraps errors with the call signature', () => { + // @ts-ignore private method + const method = api.createMethodSend(methods.blah); + + return method().catch((error) => { + expect(error.message).toMatch(/blah \(foo: Bytes\): Bytes/); + }); + }); + + it('checks for mismatched parameters', () => { + // @ts-ignore private method + const method = api.createMethodSend(methods.bleh); + + return method(1).catch((error) => { + expect(error.message).toMatch(/parameters, 1 found instead/); + }); + }); + + it('calls the provider with the correct parameters', () => { + // @ts-ignore private method + const method = api.createMethodSend(methods.blah); + + // Args are length-prefixed, because it's a Bytes + return method(new Uint8Array([2 << 2, 0x12, 0x34])).then(() => { + expect(provider.send).toHaveBeenCalledWith('test_blah', ['0x1234']); + }); + }); +}); diff --git a/packages/rpc-core/src/types.ts b/packages/rpc-core/src/types.ts new file mode 100644 index 0000000000..afdf14ffee --- /dev/null +++ b/packages/rpc-core/src/types.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export interface RpcInterface$Method { + (...params: Array): Promise; + + subscription: string; + unsubscribe: (id: number) => Promise; +} + +export type RpcInterface$Section = { + [index: string]: RpcInterface$Method +}; + +export interface RpcInterface { + readonly author: RpcInterface$Section; + readonly chain: RpcInterface$Section; + readonly state: RpcInterface$Section; + readonly system: RpcInterface$Section; +} diff --git a/packages/rpc-core/test/e2e/basics.test.js b/packages/rpc-core/test/e2e/basics.test.js new file mode 100644 index 0000000000..7b82880d51 --- /dev/null +++ b/packages/rpc-core/test/e2e/basics.test.js @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '@polkadot/rpc-provider/ws'; + +import Rpc from '../../src'; + +describe.skip('e2e basics', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + it('retrieves the pending extrinsics', () => { + return api.author + .pendingExtrinsics() + .then((extrinsics) => { + console.error('extrinsics', extrinsics); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); + + it('retrieves the system properties', () => { + return api.system + .properties() + .then((properties) => { + console.error('properties', properties); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); +}); diff --git a/packages/rpc-core/test/e2e/chain.test.js b/packages/rpc-core/test/e2e/chain.test.js new file mode 100644 index 0000000000..a40d734b18 --- /dev/null +++ b/packages/rpc-core/test/e2e/chain.test.js @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '@polkadot/rpc-provider/ws'; + +import Rpc from '../../src'; + +describe.skip('e2e chain', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + it('subscribes via subscribeNewHead', (done) => { + let count = 0; + + // tslint:disable-next-line + api.chain + .subscribeNewHead((header) => { + expect(header).toBeDefined(); + + if (++count === 3) { + done(); + } + }) + .then((subscriptionId) => { + console.log('newHead: subscriptionId =', subscriptionId); + }); + }); + + it('retrieves the runtime version', () => { + return api.chain + .getRuntimeVersion() + .then((version) => { + console.error('version', version); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); +}); diff --git a/packages/rpc-core/test/e2e/krummelanke.test.js b/packages/rpc-core/test/e2e/krummelanke.test.js new file mode 100644 index 0000000000..6e48ad1182 --- /dev/null +++ b/packages/rpc-core/test/e2e/krummelanke.test.js @@ -0,0 +1,63 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import storage from '@polkadot/storage'; +import Ws from '@polkadot/rpc-provider/ws'; + +import Rpc from '../../src'; + +describe.skip('e2e krumme lanke', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + it('subscribes to storage', (done) => { + api.state + .subscribeStorage( + [ + [storage.system.accountNonce, '5Ejbye9R8ygByQPrDSasaUid1munedPZUmg4f118HGmtodGp'], + [storage.balances.freeBalance, '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7'] + ], + (data) => { + expect(data).toHaveLength(2); + expect(data[0].toNumber()).toEqual(0); + expect(data[1].toNumber()).not.toEqual(0); + + done(); + } + ) + .then((subscriptionId) => { + console.log('stoarge subscriptionId =', subscriptionId); + }); + }); + + it('retrieves a block by hash (krumme lanke #1)', () => { + return api.chain + .getBlock('0x627847bffdf5f3e01ac440d057dec6a37a12a6f329db7ef8367665574b76b5df') + .then((block) => { + expect(block).toBeDefined(); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); + + it('retrieves a block by hash (krumme lanke #2)', () => { + return api.chain + .getBlock('0x53416d53a4b1dfcae9165a89d193608e4aa770414f02267f5b2c4015a2e66091') + .then((block) => { + expect(block).toBeDefined(); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); +}); diff --git a/packages/rpc-core/test/e2e/state.test.js b/packages/rpc-core/test/e2e/state.test.js new file mode 100644 index 0000000000..4699540c3e --- /dev/null +++ b/packages/rpc-core/test/e2e/state.test.js @@ -0,0 +1,81 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '@polkadot/rpc-provider/ws'; +import storage from '@polkadot/storage/static'; + +import Rpc from '../../src'; + +const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; + +describe.skip('e2e state', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + it('retrieves code', () => { + return api.state + .getStorage([ + storage.substrate.code + ]) + .then((code) => { + console.error(code.toHex().substr(0, 256), '...'); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); + + it('retrieves the wasm metadata', () => { + return api.state + .getMetadata() + .then((meta) => { + console.error(JSON.stringify(meta.toJSON())); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); + + it('retrieves balances', () => { + return api.state + .getStorage([ + storage.balances.freeBalance, ALICE + ]) + .then((balance) => { + console.error(balance); + + expect(balance.isZero()).not.toEqual(true); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); + + it('retrieves timestamp', () => { + return api.state + .getStorage([ + storage.timestamp.now + ]) + .then((moment) => { + console.error(moment); + + expect(moment.toNumber()).not.toEqual(0); + }) + .catch((error) => { + console.error(error); + + throw error; + }); + }); +}); diff --git a/packages/rpc-core/test/e2e/subscribe.test.js b/packages/rpc-core/test/e2e/subscribe.test.js new file mode 100644 index 0000000000..da1da3d374 --- /dev/null +++ b/packages/rpc-core/test/e2e/subscribe.test.js @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/rpc-core authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '@polkadot/rpc-provider/ws'; +import storage from '@polkadot/storage/static'; + +import Rpc from '../../src'; + +describe.skip('e2e subscriptions', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + it('retrieves current timestamp', (done) => { + let count = 0; + + return api.state + .subscribeStorage([[storage.timestamp.now]], (data) => { + expect(data).toBeDefined(); + + if (++count === 3) { + done(); + } + }) + .then((subscriptionId) => { + console.log('newHead: subscriptionId =', subscriptionId); + }); + }); +}); diff --git a/packages/rpc-provider/LICENSE b/packages/rpc-provider/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/rpc-provider/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/rpc-provider/README.md b/packages/rpc-provider/README.md new file mode 100644 index 0000000000..b227bddce5 --- /dev/null +++ b/packages/rpc-provider/README.md @@ -0,0 +1,45 @@ +# @polkadot/rpc-provider + +Generic transport providers to handle the transport of method calls to and from Polkadot clients from applications interacting with it. It provides an interface to making RPC calls and is generally, unless you are operating at a low-level and taking care of encoding and decoding of parameters/results, it won't be directly used, rather only passed to a higher-level interface. + +## Provider Selection + +There are two flavours of the providers provided, one allowing for using HTTP as a transport mechanism, the other using WebSockets. It is generally recommended to use the [[WsProvider]] since in addition to standard calls, it allows for subscriptions where all changes to state can be pushed from the node to the client. + +Both providers are usable (as is the API), in both browser-based and Node.js environments. Polyfills for unsupported functionality are automatically applied based on feature-detection. + +## Usage + +Installation - + +``` +yarn add @polkadot/rpc-provider +``` + +WebSocket Initialisation - + +```javascript +import WsProvider from '@polkadot/rpc-provider/ws'; + +// this is the actual default endpoint +const provider = new WsProvider('ws://127.0.0.1:9944'); +const version = await provider.send('client_version', []); + +console.log('client version', version); +``` + +HTTP Initialisation - + +```javascript +import { HttpProvider } from '@polkadot/rpc-provider'; + +// this is the actual default endpoint +const provider = new HttpProvider('http://127.0.0.1:9933'); +const version = await provider.send('chain_getBlockHash', []); + +console.log('latest block Hash', hash); +``` + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/rpc-provider/package.json b/packages/rpc-provider/package.json new file mode 100644 index 0000000000..bfc4d7f9ab --- /dev/null +++ b/packages/rpc-provider/package.json @@ -0,0 +1,46 @@ +{ + "name": "@plugnet/rpc-provider", + "version": "0.76.1", + "description": "Transport providers for the API", + "main": "index.js", + "keywords": [ + "Polkadot", + "JsonRPC" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-provider#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/keyring": "^0.76.1", + "@plugnet/storage": "^0.76.1", + "@plugnet/util": "^0.76.1", + "@plugnet/util-crypto": "^0.76.1", + "@types/nock": "^9.3.1", + "eventemitter3": "^3.1.0", + "isomorphic-fetch": "^2.2.1", + "websocket": "^1.0.28" + }, + "devDependencies": { + "mock-socket": "^8.0.5", + "nock": "^10.0.4" + } +} diff --git a/packages/rpc-provider/src/coder/decodeResponse.spec.ts b/packages/rpc-provider/src/coder/decodeResponse.spec.ts new file mode 100644 index 0000000000..56b79c83b9 --- /dev/null +++ b/packages/rpc-provider/src/coder/decodeResponse.spec.ts @@ -0,0 +1,50 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { JsonRpcResponse } from '../types'; +import Coder from '.'; + +describe('decodeResponse', () => { + let coder: Coder; + + beforeEach(() => { + coder = new Coder(); + }); + + it('expects a non-empty input object', () => { + expect( + () => coder.decodeResponse((undefined as any) as JsonRpcResponse) + ).toThrow(/Empty response/); + }); + + it('expects a valid jsonrpc field', () => { + expect( + () => coder.decodeResponse({} as JsonRpcResponse) + ).toThrow(/Invalid jsonrpc/); + }); + + it('expects a valid id field', () => { + expect( + () => coder.decodeResponse({ jsonrpc: '2.0' } as JsonRpcResponse) + ).toThrow(/Invalid id/); + }); + + it('expects a valid result field', () => { + expect( + () => coder.decodeResponse({ id: 1, jsonrpc: '2.0' } as JsonRpcResponse) + ).toThrow(/No result/); + }); + + it('throws any error found', () => { + expect( + () => coder.decodeResponse({ id: 1, jsonrpc: '2.0', error: { code: 123, message: 'test error' } } as JsonRpcResponse) + ).toThrow(/123: test error/); + }); + + it('returns the result', () => { + expect( + coder.decodeResponse({ id: 1, jsonrpc: '2.0', result: 'some result' } as JsonRpcResponse) + ).toEqual('some result'); + }); +}); diff --git a/packages/rpc-provider/src/coder/encodeJson.spec.ts b/packages/rpc-provider/src/coder/encodeJson.spec.ts new file mode 100644 index 0000000000..b006fc77d4 --- /dev/null +++ b/packages/rpc-provider/src/coder/encodeJson.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Coder from '.'; + +describe('encodeJson', () => { + let coder: Coder; + + beforeEach(() => { + coder = new Coder(); + }); + + it('encodes a valid JsonRPC JSON string', () => { + expect( + coder.encodeJson('method', 'params') + ).toEqual('{"id":1,"jsonrpc":"2.0","method":"method","params":"params"}'); + }); +}); diff --git a/packages/rpc-provider/src/coder/encodeObject.spec.ts b/packages/rpc-provider/src/coder/encodeObject.spec.ts new file mode 100644 index 0000000000..8923071cb7 --- /dev/null +++ b/packages/rpc-provider/src/coder/encodeObject.spec.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Coder from '.'; + +describe('encodeObject', () => { + let coder: Coder; + + beforeEach(() => { + coder = new Coder(); + }); + + it('starts with id === 0 (nothing sent)', () => { + expect(coder.getId()).toEqual(0); + }); + + it('encodes a valid JsonRPC object', () => { + expect( + coder.encodeObject('method', 'params') + ).toEqual({ + id: 1, + jsonrpc: '2.0', + method: 'method', + params: 'params' + }); + expect(coder.getId()).toEqual(1); + }); +}); diff --git a/packages/rpc-provider/src/coder/index.ts b/packages/rpc-provider/src/coder/index.ts new file mode 100644 index 0000000000..73b448f438 --- /dev/null +++ b/packages/rpc-provider/src/coder/index.ts @@ -0,0 +1,64 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { JsonRpcRequest, JsonRpcResponse, JsonRpcResponseBase$Error } from '../types'; + +import { assert, isUndefined, isNumber } from '@plugnet/util'; + +export default class RpcCoder { + private id: number = 0; + + decodeResponse (response: JsonRpcResponse): any { + assert(response, 'Empty response object received'); + assert(response.jsonrpc === '2.0', 'Invalid jsonrpc field in decoded object'); + + const isSubscription = !isUndefined(response.params) && !isUndefined(response.method); + + assert(isNumber(response.id) || (isSubscription && isNumber(response.params.subscription)), 'Invalid id field in decoded object'); + + this.checkError(response.error); + + assert(!isUndefined(response.result) || isSubscription, 'No result found in JsonRpc response'); + + if (isSubscription) { + this.checkError(response.params.error); + + return response.params.result; + } + + return response.result; + } + + encodeJson (method: string, params: any | Array): string { + return JSON.stringify( + this.encodeObject(method, params) + ); + } + + encodeObject (method: string, params: any | Array): JsonRpcRequest { + return { + id: ++this.id, + jsonrpc: '2.0', + method, + params + }; + } + + getId (): number { + return this.id; + } + + private checkError (error?: JsonRpcResponseBase$Error): void { + if (error) { + const { code, data, message } = error; + const _data = isUndefined(data) + ? '' + : ' (' + `${data}`.substr(0, 10) + ')' ; + + console.error(`${code}: ${message}${_data}`); + + throw new Error(`${code}: ${message}${_data}`); + } + } +} diff --git a/packages/rpc-provider/src/defaults.ts b/packages/rpc-provider/src/defaults.ts new file mode 100644 index 0000000000..12121f9a2c --- /dev/null +++ b/packages/rpc-provider/src/defaults.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const HTTP_URL = 'http://127.0.0.1:9933'; +const WS_URL = 'ws://127.0.0.1:9944'; + +export default { + HTTP_URL, + WS_URL +}; diff --git a/packages/rpc-provider/src/http/index.spec.js b/packages/rpc-provider/src/http/index.spec.js new file mode 100644 index 0000000000..2ce7c922c7 --- /dev/null +++ b/packages/rpc-provider/src/http/index.spec.js @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { TEST_HTTP_URL } from '../../test/mockHttp'; + +import Http from '.'; + +describe('Http', () => { + let http; + + beforeEach(() => { + http = new Http(TEST_HTTP_URL); + }); + + it('requires an http:// prefixed endpoint', () => { + expect( + () => new Http('ws://') + ).toThrow(/with 'http/); + }); + + it('allows https:// endpoints', () => { + expect( + () => new Http('https://') + ).not.toThrow(); + }); + + it('always returns isConnected true', () => { + expect(http.isConnected()).toEqual(true); + }); + + it('does not (yet) support subscribe', () => { + return http.subscribe().catch((error) => { + expect(error.message).toMatch(/does not have subscriptions/); + }); + }); + + it('does not (yet) support unsubscribe', () => { + return http.unsubscribe().catch((error) => { + expect(error.message).toMatch(/does not have subscriptions/); + }); + }); +}); diff --git a/packages/rpc-provider/src/http/index.ts b/packages/rpc-provider/src/http/index.ts new file mode 100644 index 0000000000..d006ffdd6c --- /dev/null +++ b/packages/rpc-provider/src/http/index.ts @@ -0,0 +1,128 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types'; + +import './polyfill'; + +import { assert, logger } from '@plugnet/util'; + +import Coder from '../coder'; +import defaults from '../defaults'; + +const ERROR_SUBSCRIBE = 'HTTP Provider does not have subscriptions, use WebSockets instead'; + +const l = logger('api-http'); + +/** + * # @polkadot/rpc-provider/https + * + * @name HttpProvider + * + * @description The HTTP Provider allows sending requests using HTTP to a HTTP RPC server TCP port. It does not support subscriptions so you won't be able to listen to events such as new blocks or balance changes. It is usually preferrable using the [[WsProvider]]. + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/api/promise'; + * import HttpProvider from '@plugnet/rpc-provider/http'; + * + * const provider = new HttpProvider('http://127.0.0.1:9933'); + * const api = new Api(provider); + * ``` + * + * @see [[WsProvider]] + */ +export default class HttpProvider implements ProviderInterface { + private coder: Coder; + private endpoint: string; + + /** + * @param {string} endpoint The endpoint url starting with http:// + */ + constructor (endpoint: string = defaults.HTTP_URL) { + assert(/^(https|http):\/\//.test(endpoint), `Endpoint should start with 'http://', received '${endpoint}'`); + + this.coder = new Coder(); + this.endpoint = endpoint; + } + + /** + * @summary `true` when this provider supports subscriptions + */ + get hasSubscriptions (): boolean { + return false; + } + + /** + * @description Returns a clone of the object + */ + clone (): HttpProvider { + throw new Error('Unimplemented'); + } + + /** + * @description Manually disconnect from the connection + */ + disconnect (): void { + // noop + } + + /** + * @summary Whether the node is connected or not. + * @return {boolean} true if connected + */ + isConnected (): boolean { + return true; + } + + /** + * @summary Events are not supported with the HttpProvider, see [[WsProvider]]. + * @description HTTP Provider does not have 'on' emitters. WebSockets should be used instead. + */ + on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void { + l.error(`HTTP Provider does not have 'on' emitters, use WebSockets instead`); + } + + /** + * @summary Send HTTP POST Request with Body to configured HTTP Endpoint. + */ + async send (method: string, params: Array): Promise { + const body = this.coder.encodeJson(method, params); + const response = await fetch(this.endpoint, { + body, + headers: { + 'Accept': 'application/json', + 'Content-Length': `${body.length}`, + 'Content-Type': 'application/json' + }, + method: 'POST' + }); + + assert(response.ok, `[${response.status}]: ${response.statusText}`); + + const result = await response.json(); + + return this.coder.decodeResponse(result); + } + + /** + * @summary Subscriptions are not supported with the HttpProvider, see [[WsProvider]]. + */ + async subscribe (types: string, method: string, params: Array, cb: ProviderInterface$Callback): Promise { + l.error(ERROR_SUBSCRIBE); + + throw new Error(ERROR_SUBSCRIBE); + } + + /** + * @summary Subscriptions are not supported with the HttpProvider, see [[WsProvider]]. + */ + async unsubscribe (type: string, method: string, id: number): Promise { + l.error(ERROR_SUBSCRIBE); + + throw new Error(ERROR_SUBSCRIBE); + } +} diff --git a/packages/rpc-provider/src/http/polyfill.spec.js b/packages/rpc-provider/src/http/polyfill.spec.js new file mode 100644 index 0000000000..fc7ae18b8b --- /dev/null +++ b/packages/rpc-provider/src/http/polyfill.spec.js @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('http/polyfill', () => { + let origFetch; + + beforeEach(() => { + origFetch = global.fetch; + global.fetch = null; + }); + + afterEach(() => { + global.fetch = origFetch; + }); + + it('polyfills with no exceptions (without fetch)', () => { + expect(require('./polyfill')).toBeDefined(); + }); + + it('polyfills with no exceptions (with fetch)', () => { + global.fetch = () => true; + + expect(require('./polyfill')).toBeDefined(); + }); +}); diff --git a/packages/rpc-provider/src/http/polyfill.ts b/packages/rpc-provider/src/http/polyfill.ts new file mode 100644 index 0000000000..35fb7b140d --- /dev/null +++ b/packages/rpc-provider/src/http/polyfill.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// tslint:disable-next-line +if (typeof fetch === 'undefined') { + require('isomorphic-fetch'); +} diff --git a/packages/rpc-provider/src/http/send.spec.js b/packages/rpc-provider/src/http/send.spec.js new file mode 100644 index 0000000000..eb3424a933 --- /dev/null +++ b/packages/rpc-provider/src/http/send.spec.js @@ -0,0 +1,56 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockHttp, TEST_HTTP_URL } from '../../test/mockHttp'; + +import Http from '.'; + +describe('send', () => { + let http; + let mock; + + beforeEach(() => { + http = new Http(TEST_HTTP_URL); + }); + + afterEach(() => { + if (mock) { + mock.done(); + mock = null; + } + }); + + it('passes the body through correctly', () => { + mock = mockHttp([{ + method: 'test_body', + reply: { + result: 'ok' + } + }]); + + return http + .send('test_body', ['param']) + .then((result) => { + expect(mock.body['test_body']).toEqual({ + id: 1, + jsonrpc: '2.0', + method: 'test_body', + params: ['param'] + }); + }); + }); + + it('throws error when !response.ok', () => { + mock = mockHttp([{ + code: 500, + method: 'test_error' + }]); + + return http + .send('test_error', []) + .catch((error) => { + expect(error.message).toMatch(/\[500\]: Internal Server/); + }); + }); +}); diff --git a/packages/rpc-provider/src/http/types.ts b/packages/rpc-provider/src/http/types.ts new file mode 100644 index 0000000000..e22ab17a3f --- /dev/null +++ b/packages/rpc-provider/src/http/types.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Logger } from '@plugnet/util/types'; + +import Coder from '../coder'; + +export type HttpState = { + coder: Coder, + endpoint: string, + l: Logger +}; diff --git a/packages/rpc-provider/src/index.ts b/packages/rpc-provider/src/index.ts new file mode 100644 index 0000000000..2961cf4845 --- /dev/null +++ b/packages/rpc-provider/src/index.ts @@ -0,0 +1,6 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { default as HttpProvider } from './http'; +export { default as WsProvider } from './ws'; diff --git a/packages/rpc-provider/src/mock/index.ts b/packages/rpc-provider/src/mock/index.ts new file mode 100644 index 0000000000..6661693701 --- /dev/null +++ b/packages/rpc-provider/src/mock/index.ts @@ -0,0 +1,206 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types'; +import { MockState$Subscriptions, MockState$Subscription$Callback, MockState$Db } from './types'; + +import BN from 'bn.js'; +import EventEmitter from 'eventemitter3'; +import interfaces from '@plugnet/jsonrpc'; +import testKeyring from '@plugnet/keyring/testing'; +import storage from '@plugnet/storage/static'; +import { Codec } from '@plugnet/types/types'; +import metadata from '@plugnet/types/Metadata/v0/static'; +import { Header } from '@plugnet/types'; +import { bnToU8a, logger, u8aToHex } from '@plugnet/util'; +import { randomAsU8a } from '@plugnet/util-crypto'; + +const INTERVAL = 1000; +const SUBSCRIPTIONS: string[] = Array.prototype.concat.apply( + [], Object.values(interfaces).map((area) => + Object + .values(area.methods) + .filter((method) => + method.isSubscription + ) + .map(({ method, section }) => + `${section}_${method}` + ) + ) +); + +const keyring = testKeyring({ type: 'ed25519' }); +const l = logger('api-mock'); + +/** + * A mock provider mainly used for testing. + * @return {ProviderInterface} The mock provider + */ +export default class Mock implements ProviderInterface { + private db: MockState$Db = {}; + private emitter = new EventEmitter(); + public isUpdating: boolean = true; + private requests: { [index: string]: (...params: any[]) => string } = { + 'chain_getBlockHash': (blockNumber: number): string => '0x1234', + 'chain_getRuntimeVersion': (): string => '0x1234', + 'state_getStorage': (storage: MockState$Db, params: Array): string => { + return u8aToHex( + storage[(params[0] as string)] + ); + }, + 'system_chain': (): string => 'mockChain', + 'state_getMetadata': (): string => metadata, + 'system_name': (): string => 'mockClient', + 'system_version': (): string => '9.8.7' + }; + public subscriptions: MockState$Subscriptions = SUBSCRIPTIONS.reduce((subs, name) => { + subs[name] = { + callbacks: {}, + lastValue: null + }; + + return subs; + }, ({} as MockState$Subscriptions)); + private subscriptionId: number = 0; + private subscriptionMap: { + [index: number]: string + } = {}; + + constructor () { + this.init(); + } + + get hasSubscriptions (): boolean { + return true; + } + + clone (): Mock { + throw new Error('Unimplemented'); + } + + disconnect (): void { + // noop + } + + isConnected (): boolean { + return true; + } + + on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void { + this.emitter.on(type, sub); + } + + async send (method: string, params: Array): Promise { + if (!this.requests[method]) { + throw new Error(`provider.send: Invalid method '${method}'`); + } + + return this.requests[method](this.db, params); + } + + async subscribe (type: string, method: string, ...params: Array): Promise { + l.debug(() => ['subscribe', method, params]); + + if (this.subscriptions[method]) { + const callback: MockState$Subscription$Callback = params.pop(); + const id = ++this.subscriptionId; + + this.subscriptions[method].callbacks[id] = callback; + this.subscriptionMap[id] = method; + + if (this.subscriptions[method].lastValue !== null) { + callback(null, this.subscriptions[method].lastValue); + } + + return id; + } + + throw new Error(`provider.subscribe: Invalid method '${method}'`); + } + + async unsubscribe (type: string, method: string, id: number): Promise { + const sub = this.subscriptionMap[id]; + + l.debug(() => ['unsubscribe', id, sub]); + + if (!sub) { + throw new Error(`Unable to find subscription for ${id}`); + } + + delete this.subscriptionMap[id]; + delete this.subscriptions[sub].callbacks[id]; + + return true; + } + + private init () { + const emitEvents: Array = ['connected', 'disconnected']; + let emitIndex = 0; + let newHead = this.makeBlockHeader(new BN(-1)); + let counter = -1; + + // Do something every 1 seconds + setInterval(() => { + if (!this.isUpdating) { + return; + } + + // create a new header (next block) + newHead = this.makeBlockHeader(newHead.blockNumber.toBn()); + + // increment the balances and nonce for each account + keyring.getPairs().forEach(({ publicKey }, index) => { + this.setStateBn(storage.balances.freeBalance(publicKey()), newHead.blockNumber.muln(3).iaddn(index)); + this.setStateBn(storage.system.accountNonce(publicKey()), newHead.blockNumber.addn(index)); + }); + + // set the timestamp for the current block + this.setStateBn(storage.timestamp.now(), Math.floor(Date.now() / 1000)); + this.updateSubs('chain_subscribeNewHead', newHead); + + // We emit connected/disconnected at intervals + if (++counter % 2 === 1) { + if (++emitIndex === emitEvents.length) { + emitIndex = 0; + } + + this.emitter.emit(emitEvents[emitIndex]); + } + }, INTERVAL); + } + + private makeBlockHeader (prevNumber: BN): Header { + const blockNumber = prevNumber.addn(1); + + return new Header({ + digest: { + logs: [] + }, + extrinsicsRoot: randomAsU8a(), + number: blockNumber, + parentHash: blockNumber.isZero() + ? new Uint8Array(32) + : bnToU8a(prevNumber, 256, false), + stateRoot: bnToU8a(blockNumber, 256, false) + }); + } + + private setStateBn (key: Uint8Array, value: BN | number): void { + this.db[u8aToHex(key)] = bnToU8a(value, 64, true); + } + + private updateSubs (method: string, value: Codec) { + this.subscriptions[method].lastValue = value; + + Object + .values(this.subscriptions[method].callbacks) + .forEach((cb) => { + try { + cb(null, value.toJSON()); + } catch (error) { + console.error(`Error on '${method}' subscription`, error); + } + }); + } +} diff --git a/packages/rpc-provider/src/mock/on.spec.ts b/packages/rpc-provider/src/mock/on.spec.ts new file mode 100644 index 0000000000..f5143d2511 --- /dev/null +++ b/packages/rpc-provider/src/mock/on.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ProviderInterface$Emitted } from '../types'; + +import Mock from '.'; + +describe('on', () => { + let mock: Mock; + + beforeEach(() => { + mock = new Mock(); + }); + + it('it emits both connected and disconnected events', (done) => { + const events: { [index: string]: boolean } = { connected: false, disconnected: false }; + const handler = (type: ProviderInterface$Emitted) => { + mock.on(type, () => { + events[type] = true; + + if (Object.values(events).filter((value) => value).length === 2) { + done(); + } + }); + }; + + handler('connected'); + handler('disconnected'); + }); +}); diff --git a/packages/rpc-provider/src/mock/send.spec.ts b/packages/rpc-provider/src/mock/send.spec.ts new file mode 100644 index 0000000000..c684d18168 --- /dev/null +++ b/packages/rpc-provider/src/mock/send.spec.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Mock from '.'; + +describe('send', () => { + let mock: Mock; + + beforeEach(() => { + mock = new Mock(); + }); + + it('fails on non-supported methods', () => { + return mock + .send('something_invalid', []) + .catch((error) => { + expect(error.message).toMatch(/Invalid method/); + }); + }); + + it('returns values for mocked requests', () => { + return mock + .send('system_name', []) + .then((result) => { + expect(result).toBe('mockClient'); + }); + }); +}); diff --git a/packages/rpc-provider/src/mock/subscribe.spec.ts b/packages/rpc-provider/src/mock/subscribe.spec.ts new file mode 100644 index 0000000000..3d298abbcc --- /dev/null +++ b/packages/rpc-provider/src/mock/subscribe.spec.ts @@ -0,0 +1,63 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Mock from '.'; + +describe('subscribe', () => { + let mock: Mock; + + beforeEach(() => { + mock = new Mock(); + }); + + it('fails on unknown methods', () => { + return mock + .subscribe('test', 'test_notFound') + .catch((error) => { + expect(error.message).toMatch(/Invalid method 'test_notFound'/); + }); + }); + + it('returns a subscription id', () => { + return mock + .subscribe('chain_newHead', 'chain_subscribeNewHead', () => void 0) + .then((id) => { + expect(id).toEqual(1); + }); + }); + + it('calls back with the last known value', (done) => { + mock.isUpdating = false; + mock.subscriptions['chain_subscribeNewHead'].lastValue = 'testValue'; + + return mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, value: string) => { + expect(value).toEqual('testValue'); + done(); + }); + }); + + it('calls back with new headers', (done) => { + return mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: any) => { + if (header.number === 4) { + done(); + } + }); + }); + + it('handles errors withing callbacks gracefully', (done) => { + let hasThrown: boolean = false; + + return mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: any) => { + if (!hasThrown) { + hasThrown = true; + + throw new Error('testing'); + } + + if (header.number === 3) { + done(); + } + }); + }); +}); diff --git a/packages/rpc-provider/src/mock/types.ts b/packages/rpc-provider/src/mock/types.ts new file mode 100644 index 0000000000..7ddd7b1eba --- /dev/null +++ b/packages/rpc-provider/src/mock/types.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type MockState$Subscription$Callback = (error: Error | null, value: any) => void; + +export type MockState$Subscriptions = { + [index: string]: { + callbacks: { + [index: number]: MockState$Subscription$Callback + }, + lastValue: any + } +}; + +export type MockState$Db = { + [index: string]: Uint8Array +}; + +export type MockState$Requests = { + [index: string]: (db: MockState$Db, params: Array) => string +}; diff --git a/packages/rpc-provider/src/mock/unsubscribe.spec.ts b/packages/rpc-provider/src/mock/unsubscribe.spec.ts new file mode 100644 index 0000000000..cdceda9db0 --- /dev/null +++ b/packages/rpc-provider/src/mock/unsubscribe.spec.ts @@ -0,0 +1,40 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Mock from '.'; + +describe('unsubscribe', () => { + let mock: Mock; + let id: number; + + beforeEach(() => { + mock = new Mock(); + + return mock + .subscribe('chain_newHead', 'chain_subscribeNewHead', () => void 0) + .then((_id) => { + id = _id; + }); + }); + + it('fails on unknown ids', () => { + return mock + .unsubscribe('chain_newHead', 'chain_subscribeNewHead', 5) + .catch((error) => { + expect(error.message).toMatch(/Unable to find/); + }); + }); + + it('unsubscribes successfully', () => { + return mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id); + }); + + it('fails on double unsubscribe', () => { + return mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id) + .then(() => mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id)) + .catch((error) => { + expect(error.message).toMatch(/Unable to find/); + }); + }); +}); diff --git a/packages/rpc-provider/src/types.ts b/packages/rpc-provider/src/types.ts new file mode 100644 index 0000000000..9515afa540 --- /dev/null +++ b/packages/rpc-provider/src/types.ts @@ -0,0 +1,54 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type JsonRpcObject = { + id: number; + jsonrpc: '2.0'; +}; + +export type JsonRpcRequest = JsonRpcObject & { + method: string; + params: Array; +}; + +export type JsonRpcResponseBase$Error = { + code: number, + data?: number | string, + message: string +}; + +type JsonRpcResponse$Single = { + error?: JsonRpcResponseBase$Error; + result?: any; +}; + +type JsonRpcResponse$Subscription = { + method?: string; + params: { + error?: JsonRpcResponseBase$Error; + result: any; + subscription: number; + } +}; + +export type JsonRpcResponseBase = JsonRpcResponse$Single & JsonRpcResponse$Subscription; + +export type JsonRpcResponse = JsonRpcObject & JsonRpcResponseBase; + +export type ProviderInterface$Callback = (result: any) => void; + +export type ProviderInterface$Emitted = 'connected' | 'disconnected' | 'error'; + +export type ProviderInterface$EmitCb = (value?: any) => any; + +export interface ProviderInterface { + readonly hasSubscriptions: boolean; + clone (): ProviderInterface; + disconnect (): void; + isConnected (): boolean; + on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void; + send (method: string, params: Array): Promise; + subscribe (type: string, method: string, params: Array, cb: ProviderInterface$Callback): Promise; + unsubscribe (type: string, method: string, id: number): Promise; +} diff --git a/packages/rpc-provider/src/ws/Provider.ts b/packages/rpc-provider/src/ws/Provider.ts new file mode 100644 index 0000000000..c1434f3cc9 --- /dev/null +++ b/packages/rpc-provider/src/ws/Provider.ts @@ -0,0 +1,412 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { JsonRpcResponse, ProviderInterface, ProviderInterface$Callback, ProviderInterface$Emitted, ProviderInterface$EmitCb } from '../types'; + +import './polyfill'; + +import EventEmitter from 'eventemitter3'; +import { assert, isNull, isUndefined, logger } from '@plugnet/util'; + +import Coder from '../coder'; +import defaults from '../defaults'; + +type CallbackHandler = (error?: null | Error, value?: any) => void; + +type SubscriptionHandler = { + callback: CallbackHandler, + type: string +}; + +type WsState$Awaiting = { + callback: CallbackHandler, + method: string, + params: Array, + subscription?: SubscriptionHandler +}; + +type WsState$Subscription = SubscriptionHandler & { + method: string, + params: Array +}; + +interface WSProviderInterface extends ProviderInterface { + connect (): void; +} + +const ALIASSES: { [index: string]: string } = { + 'chain_finalisedHead': 'chain_finalizedHead' +}; + +const l = logger('api-ws'); + +/** + * # @polkadot/rpc-provider/ws + * + * @name WsProvider + * + * @description The WebSocket Provider allows sending requests using WebSocket to a WebSocket RPC server TCP port. Unlike the [[HttpProvider]], it does support subscriptions and allows listening to events such as new blocks or balance changes. + * + * @example + *
+ * + * ```javascript + * import Api from '@plugnet/api/promise'; + * import WsProvider from '@plugnet/rpc-provider/ws'; + * + * const provider = new WsProvider('ws://127.0.0.1:9944'); + * const api = new Api(provider); + * ``` + * + * @see [[HttpProvider]] + */ +export default class WsProvider implements WSProviderInterface { + private _eventemitter: EventEmitter; + private _isConnected: boolean = false; + private autoConnect: boolean; + private coder: Coder; + private endpoint: string; + private handlers: { + [index: string]: WsState$Awaiting + }; + private queued: { + [index: string]: string + }; + private subscriptions: { + [index: string]: WsState$Subscription + }; + private waitingForId: { + [index: string]: JsonRpcResponse + }; + private websocket: WebSocket | null; + + /** + * @param {string} endpoint The endpoint url. Usually `ws://ip:9944` or `wss://ip:9944` + * @param {boolean} autoConnect Whether to connect automatically or not. + */ + constructor (endpoint: string = defaults.WS_URL, autoConnect: boolean = true) { + assert(/^(wss|ws):\/\//.test(endpoint), `Endpoint should start with 'ws://', received '${endpoint}'`); + + this._eventemitter = new EventEmitter(); + this.autoConnect = autoConnect; + this.coder = new Coder(); + this.endpoint = endpoint; + this.handlers = {}; + this.queued = {}; + this.subscriptions = {}; + this.waitingForId = {}; + this.websocket = null; + + if (autoConnect) { + this.connect(); + } + } + + /** + * @summary `true` when this provider supports subscriptions + */ + get hasSubscriptions (): boolean { + return true; + } + + /** + * @description Returns a clone of the object + */ + clone (): WsProvider { + return new WsProvider(this.endpoint); + } + + /** + * @summary Manually connect + * @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may + * connect manually using this method. + */ + connect (): void { + try { + this.websocket = new WebSocket(this.endpoint); + + this.websocket.onclose = this.onSocketClose; + this.websocket.onerror = this.onSocketError; + this.websocket.onmessage = this.onSocketMessage; + this.websocket.onopen = this.onSocketOpen; + } catch (error) { + l.error(error); + } + } + + /** + * @description Manually disconnect from the connection, clearing autoconnect logic + */ + disconnect (): void { + if (isNull(this.websocket)) { + throw new Error('Cannot disconnect on a non-open websocket'); + } + + // switch off autoConnect, we are in manual mode now + this.autoConnect = false; + + // 1000 - Normal closure; the connection successfully completed + this.websocket.close(1000); + this.websocket = null; + } + + /** + * @summary Whether the node is connected or not. + * @return {boolean} true if connected + */ + isConnected (): boolean { + return this._isConnected; + } + + /** + * @summary Listens on events after having subscribed using the [[subscribe]] function. + * @param {ProviderInterface$Emitted} type Event + * @param {ProviderInterface$EmitCb} sub Callback + */ + on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void { + this._eventemitter.on(type, sub); + } + + /** + * @summary Send JSON data using WebSockets to configured HTTP Endpoint or queue. + * @param method The RPC methods to execute + * @param params Encoded paramaters as appliucable for the method + * @param subscription Subscription details (internally used) + */ + async send (method: string, params: Array, subscription?: SubscriptionHandler): Promise { + return new Promise((resolve, reject): void => { + try { + const json = this.coder.encodeJson(method, params); + const id = this.coder.getId(); + const callback = (error?: Error | null, result?: any) => { + error + ? reject(error) + : resolve(result); + }; + + l.debug(() => ['calling', method, params, json, !!subscription]); + + this.handlers[id] = { + callback, + method, + params, + subscription + }; + + if (this.isConnected() && !isNull(this.websocket)) { + this.websocket.send(json); + } else { + this.queued[id] = json; + } + } catch (error) { + reject(error); + } + }); + } + + /** + * @name subscribe + * @summary Allows subscribing to a specific event. + * @param {string} type Subscription type + * @param {string} method Subscription method + * @param {Array} params Parameters + * @param {ProviderInterface$Callback} callback Callback + * @return {Promise} Promise resolving to the dd of the subscription you can use with [[unsubscribe]]. + * + * @example + *
+ * + * ```javascript + * const provider = new WsProvider('ws://127.0.0.1:9944'); + * const rpc = new Rpc(provider); + * + * rpc.state.subscribeStorage([[storage.balances.freeBalance,
]], (_, values) => { + * console.log(values) + * }).then((subscriptionId) => { + * console.log('balance changes subscription id: ', subscriptionId) + * }) + * ``` + */ + async subscribe (type: string, method: string, params: Array, callback: ProviderInterface$Callback): Promise { + const id = await this.send(method, params, { callback, type }); + + return id as number; + } + + /** + * @summary Allows unsubscribing to subscriptions made with [[subscribe]]. + */ + async unsubscribe (type: string, method: string, id: number): Promise { + const subscription = `${type}::${id}`; + + // FIXME This now could happen with re-subscriptions. The issue is that with a re-sub + // the assigned id now does not match what the API user originally received. It has + // a slight complication in solving - since we cannot rely on the send id, but rather + // need to find the actual subscription id to map it + if (isUndefined(this.subscriptions[subscription])) { + l.debug(() => `Unable to find active subscription=${subscription}`); + + return false; + } + + delete this.subscriptions[subscription]; + + const result = await this.send(method, [id]); + + return result as boolean; + } + + private emit (type: ProviderInterface$Emitted, ...args: Array): void { + this._eventemitter.emit(type, ...args); + } + + private onSocketClose = (event: CloseEvent): void => { + if (this.autoConnect) { + l.error(`disconnected from ${this.endpoint} code: '${event.code}' reason: '${event.reason}'`); + } + + this._isConnected = false; + this.emit('disconnected'); + + if (this.autoConnect) { + setTimeout(() => { + this.connect(); + }, 1000); + } + } + + private onSocketError = (error: Event): void => { + l.debug(() => ['socket error', error]); + this.emit('error', error); + } + + private onSocketMessage = (message: MessageEvent): void => { + l.debug(() => ['received', message.data]); + + const response: JsonRpcResponse = JSON.parse(message.data as string); + + return isUndefined(response.method) + ? this.onSocketMessageResult(response) + : this.onSocketMessageSubscribe(response); + } + + private onSocketMessageResult = (response: JsonRpcResponse): void => { + l.debug(() => ['handling: response =', response, 'id =', response.id]); + + const handler = this.handlers[response.id]; + + if (!handler) { + l.debug(() => `Unable to find handler for id=${response.id}`); + return; + } + + try { + const { method, params, subscription } = handler; + const result = this.coder.decodeResponse(response); + + // first send the result - in case of subs, we may have an update + // immediately if we have some queued results already + handler.callback(null, result); + + if (subscription) { + const subId = `${subscription.type}::${result}`; + + this.subscriptions[subId] = { + ...subscription, + method, + params + }; + + // if we have a result waiting for this subscription already + if (this.waitingForId[subId]) { + this.onSocketMessageSubscribe(this.waitingForId[subId]); + } + } + } catch (error) { + handler.callback(error, undefined); + } + + delete this.handlers[response.id]; + } + + private onSocketMessageSubscribe = (response: JsonRpcResponse): void => { + const method = ALIASSES[response.method as string] || response.method; + const subId = `${method}::${response.params.subscription}`; + + l.debug(() => ['handling: response =', response, 'subscription =', subId]); + + const handler = this.subscriptions[subId]; + + if (!handler) { + // store the JSON, we could have out-of-order subid coming in + this.waitingForId[subId] = response; + + l.debug(() => `Unable to find handler for subscription=${subId}`); + return; + } + + // housekeeping + delete this.waitingForId[subId]; + + try { + const result = this.coder.decodeResponse(response); + + handler.callback(null, result); + } catch (error) { + handler.callback(error, undefined); + } + } + + private onSocketOpen = (): boolean => { + assert(!isNull(this.websocket), 'WebSocket cannot be null in onOpen'); + + l.debug(() => ['connected to', this.endpoint]); + + this._isConnected = true; + this.emit('connected'); + + this.sendQueue(); + this.resubscribe(); + + return true; + } + + private resubscribe (): void { + const subscriptions = this.subscriptions; + + this.subscriptions = {}; + + Object.keys(subscriptions).forEach(async (id) => { + const { callback, method, params, type } = subscriptions[id]; + + // only re-create subscriptions which are not in author (only area where + // transactions are created, i.e. submissions such as 'author_submitAndWatchExtrinsic' + // are not included (and will not be re-broadcast) + if (type.startsWith('author_')) { + return; + } + + try { + await this.subscribe(type, method, params, callback); + } catch (error) { + l.error(error); + } + }); + } + + private sendQueue (): void { + Object.keys(this.queued).forEach((id) => { + try { + // @ts-ignore we have done the websocket check in onSocketOpen, if an issue, will catch it + this.websocket.send( + this.queued[id] + ); + + delete this.queued[id]; + } catch (error) { + l.error(error); + } + }); + } +} diff --git a/packages/rpc-provider/src/ws/connect.spec.js b/packages/rpc-provider/src/ws/connect.spec.js new file mode 100644 index 0000000000..6a6f0aff9f --- /dev/null +++ b/packages/rpc-provider/src/ws/connect.spec.js @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +describe('onConnect', () => { + let mock; + + beforeEach(() => { + mock = mockWs([]); + }); + + afterEach(() => { + if (mock) { + mock.done(); + mock = null; + } + }); + + it('sets up the on* handlers', () => { + const ws = new Ws(TEST_WS_URL, false); + + ws.connect(); + + expect(ws.websocket.onclose[0]).toBeDefined(); + expect(ws.websocket.onerror[0]).toBeDefined(); + expect(ws.websocket.onmessage[0]).toBeDefined(); + expect(ws.websocket.onopen[0]).toBeDefined(); + }); +}); diff --git a/packages/rpc-provider/src/ws/index.spec.js b/packages/rpc-provider/src/ws/index.spec.js new file mode 100644 index 0000000000..480ca11a2e --- /dev/null +++ b/packages/rpc-provider/src/ws/index.spec.js @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +let ws; +let mock; + +function createWs (requests, autoConnect) { + mock = mockWs(requests); + ws = new Ws(TEST_WS_URL, autoConnect); + + return ws; +} + +describe('Ws', () => { + afterEach(() => { + if (mock) { + mock.done(); + mock = null; + } + }); + + it('returns the connected state', () => { + expect( + createWs([]).isConnected() + ).toEqual(false); + }); +}); diff --git a/packages/rpc-provider/src/ws/index.ts b/packages/rpc-provider/src/ws/index.ts new file mode 100644 index 0000000000..2e62a42dd7 --- /dev/null +++ b/packages/rpc-provider/src/ws/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as WsProvider } from './Provider'; + +export default WsProvider; diff --git a/packages/rpc-provider/src/ws/onMessageResult.spec.js b/packages/rpc-provider/src/ws/onMessageResult.spec.js new file mode 100644 index 0000000000..e3b5a09d12 --- /dev/null +++ b/packages/rpc-provider/src/ws/onMessageResult.spec.js @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '.'; + +describe('onMessageResult', () => { + let ws; + + beforeEach(() => { + ws = new Ws('ws://127.0.0.1:1234', false); + }); + + it('calls the handler when found', (done) => { + ws.handlers[5] = { + callback: (_, result) => { + expect(result).toEqual('test'); + done(); + } + }; + + ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":5,"result":"test"}' }); + }); +}); diff --git a/packages/rpc-provider/src/ws/onMessageSubscribe.spec.js b/packages/rpc-provider/src/ws/onMessageSubscribe.spec.js new file mode 100644 index 0000000000..0f139b1d02 --- /dev/null +++ b/packages/rpc-provider/src/ws/onMessageSubscribe.spec.js @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '.'; + +describe('onMessageSubscribe', () => { + let ws; + + beforeEach(() => { + ws = new Ws('ws://127.0.0.1:1234', false); + }); + + it('calls the subscriber with data', (done) => { + ws.handlers[11] = { + callback: (_, id) => {}, + method: 'test', + subscription: { + callback: (_, result) => { + expect(result).toEqual('test'); + done(); + }, + type: 'test' + } + }; + + ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":11,"result":22}' }); + ws.onSocketMessage({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"result":"test"}}' }); + }); + + it('calls the subscriber with error', (done) => { + ws.handlers[11] = { + callback: (_, id) => {}, + method: 'test', + subscription: { + callback: (error) => { + expect(error.message).toMatch(/test/); + done(); + }, + type: 'test' + } + }; + + ws.onSocketMessage({ data: '{"jsonrpc":"2.0","id":11,"result":22}' }); + ws.onSocketMessage({ data: '{"jsonrpc":"2.0","method":"test","params":{"subscription":22,"error":{"message":"test"}}}' }); + }); +}); diff --git a/packages/rpc-provider/src/ws/onOpen.spec.js b/packages/rpc-provider/src/ws/onOpen.spec.js new file mode 100644 index 0000000000..4615711443 --- /dev/null +++ b/packages/rpc-provider/src/ws/onOpen.spec.js @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +let ws; +let mock; + +function createWs (requests, autoConnect) { + mock = mockWs(requests); + ws = new Ws(TEST_WS_URL, autoConnect); + + return ws; +} + +describe('onOpen', () => { + afterEach(() => { + if (mock) { + mock.done(); + mock = null; + } + }); + + it('sends messages when connected', () => { + const ws = createWs([{ + id: 1, + method: 'test_queue', + reply: { + result: 'ok' + } + }], false); + const sendPromise = ws.send('test_queue', []); + + ws.connect(); + + return sendPromise.then((result) => { + expect(result).toEqual('ok'); + }); + }); +}); diff --git a/packages/rpc-provider/src/ws/polyfill.spec.js b/packages/rpc-provider/src/ws/polyfill.spec.js new file mode 100644 index 0000000000..d9695a0b28 --- /dev/null +++ b/packages/rpc-provider/src/ws/polyfill.spec.js @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('ws/polyfill', () => { + let origWs; + + beforeEach(() => { + origWs = global.WebSocket; + global.WebSocket = null; + }); + + afterEach(() => { + global.WebSocket = origWs; + }); + + it('polyfills with no exceptions (without WebSocket)', () => { + expect(require('./polyfill')).toBeDefined(); + }); + + it('polyfills with no exceptions (with WebSocket)', () => { + global.WebSocket = () => true; + + expect(require('./polyfill')).toBeDefined(); + }); +}); diff --git a/packages/rpc-provider/src/ws/polyfill.ts b/packages/rpc-provider/src/ws/polyfill.ts new file mode 100644 index 0000000000..96d0c00368 --- /dev/null +++ b/packages/rpc-provider/src/ws/polyfill.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// tslint:disable-next-line +if (typeof WebSocket === 'undefined') { + // @ts-ignore + global.WebSocket = require('websocket').w3cwebsocket; +} diff --git a/packages/rpc-provider/src/ws/send.spec.js b/packages/rpc-provider/src/ws/send.spec.js new file mode 100644 index 0000000000..94149065ed --- /dev/null +++ b/packages/rpc-provider/src/ws/send.spec.js @@ -0,0 +1,112 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +let ws; +let mock; + +function createMock (requests) { + mock = mockWs(requests); +} + +function createWs (autoConnect) { + ws = new Ws(TEST_WS_URL, autoConnect); + + return ws; +} + +describe('send', () => { + let globalWs; + + beforeEach(() => { + globalWs = global.WebSocket; + }); + + afterEach(() => { + global.WebSocket = globalWs; + + if (mock) { + mock.done(); + mock = null; + } + }); + + it('handles internal errors', () => { + createMock([]); + + let websocket; + + global.WebSocket = class { + constructor () { + websocket = this; + } + + send () { + throw new Error('send error'); + } + }; + + ws = createWs(); + websocket.onopen(); + + return ws + .send('test_encoding', ['param']) + .catch((error) => { + expect(error.message).toEqual('send error'); + }); + }); + + it('passes the body through correctly', () => { + createMock([{ + id: 1, + method: 'test_body', + reply: { + result: 'ok' + } + }]); + + return createWs() + .send('test_body', ['param']) + .then((result) => { + expect( + mock.body['test_body'] + ).toEqual('{"id":1,"jsonrpc":"2.0","method":"test_body","params":["param"]}'); + }); + }); + + it('throws error when !response.ok', () => { + createMock([{ + id: 1, + error: { + code: 666, + message: 'error' + } + }]); + + return createWs() + .send('test_error', []) + .catch((error) => { + expect(error.message).toMatch(/666: error/); + }); + }); + + it('adds subscriptions', () => { + createMock([{ + id: 1, + method: 'test_sub', + reply: { + result: 1 + } + }]); + + return createWs() + .send('test_sub', [], () => {}) + .then((id) => { + expect(id).toEqual(1); + }); + }); +}); diff --git a/packages/rpc-provider/src/ws/state.spec.js b/packages/rpc-provider/src/ws/state.spec.js new file mode 100644 index 0000000000..b47418acc4 --- /dev/null +++ b/packages/rpc-provider/src/ws/state.spec.js @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '.'; + +describe('state', () => { + it('requires an ws:// prefixed endpoint', () => { + expect( + () => new Ws('http://', false) + ).toThrow(/with 'ws/); + }); + + it('allows wss:// endpoints', () => { + expect( + () => new Ws('wss://', false) + ).not.toThrow(); + }); +}); diff --git a/packages/rpc-provider/src/ws/subscribe.spec.js b/packages/rpc-provider/src/ws/subscribe.spec.js new file mode 100644 index 0000000000..02ba4fb97e --- /dev/null +++ b/packages/rpc-provider/src/ws/subscribe.spec.js @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +let ws; +let mock; + +function createMock (requests) { + mock = mockWs(requests); +} + +function createWs (autoConnect) { + ws = new Ws(TEST_WS_URL, autoConnect); + + return ws; +} + +describe('subscribe', () => { + let globalWs; + + beforeEach(() => { + globalWs = global.WebSocket; + }); + + afterEach(() => { + global.WebSocket = globalWs; + + if (mock) { + mock.done(); + mock = null; + } + }); + + it('adds subscriptions', () => { + createMock([{ + id: 1, + method: 'test_sub', + reply: { + result: 1 + } + }]); + + return createWs() + .subscribe('test_sub', [], () => {}) + .then((id) => { + expect(id).toEqual(1); + }); + }); +}); diff --git a/packages/rpc-provider/src/ws/unsubscribe.spec.js b/packages/rpc-provider/src/ws/unsubscribe.spec.js new file mode 100644 index 0000000000..e307beeea2 --- /dev/null +++ b/packages/rpc-provider/src/ws/unsubscribe.spec.js @@ -0,0 +1,85 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mockWs, TEST_WS_URL } from '../../test/mockWs'; + +import Ws from '.'; + +let ws; +let mock; + +function createMock (requests) { + mock = mockWs(requests); +} + +function createWs (autoConnect) { + ws = new Ws(TEST_WS_URL, autoConnect); + + return ws; +} + +describe('subscribe', () => { + let globalWs; + + beforeEach(() => { + globalWs = global.WebSocket; + }); + + afterEach(() => { + global.WebSocket = globalWs; + + if (mock) { + mock.done(); + mock = null; + } + }); + + it('removes subscriptions', () => { + createMock([ + { + id: 1, + method: 'subscribe_test', + reply: { + result: 1 + } + }, + { + id: 2, + method: 'unsubscribe_test', + reply: { + result: true + } + } + ]); + + const ws = createWs(); + + return ws + .subscribe('test', 'subscribe_test', [], () => {}) + .then((id) => { + return ws.unsubscribe('test', 'subscribe_test', id); + }); + }); + + it('fails when sub not found', () => { + createMock([{ + id: 1, + method: 'subscribe_test', + reply: { + result: 1 + } + }]); + + const ws = createWs(); + + return ws + .subscribe('test', 'subscribe_test', [], () => {}) + .then((id) => { + return ws.unsubscribe('test', 'subscribe_test', 111); + }) + .then((result) => { + expect(result).toBe(false); + }); + }); +}); diff --git a/packages/rpc-provider/test/mockHttp.ts b/packages/rpc-provider/test/mockHttp.ts new file mode 100644 index 0000000000..ce3da900c5 --- /dev/null +++ b/packages/rpc-provider/test/mockHttp.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nock from 'nock'; + +const TEST_HTTP_URL = 'http://localhost:9944'; + +function mockHttp (requests: any[]) { + nock.cleanAll(); + + return requests.reduce((scope, request: any, index: number) => { + return scope + .post('/') + .reply(request.code || 200, (uri: string, body: { id: string }) => { + scope.body = scope.body || {}; + scope.body[request.method] = body; + + return Object.assign({ id: body.id, jsonrpc: '2.0' }, request.reply || {}); + }); + }, nock(TEST_HTTP_URL)); +} + +export { + TEST_HTTP_URL, + mockHttp +}; diff --git a/packages/rpc-provider/test/mockWs.ts b/packages/rpc-provider/test/mockWs.ts new file mode 100644 index 0000000000..5d80dd5605 --- /dev/null +++ b/packages/rpc-provider/test/mockWs.ts @@ -0,0 +1,83 @@ +// Copyright 2017-2019 @polkadot/rpc-provider authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Server } from 'mock-socket'; + +const TEST_WS_URL = 'ws://localhost:9955'; + +let server: Server; + +type ErrorDef = { + id: number, + error: { + code: number, + message: string + } +}; + +type ReplyDef = { + id: number, + reply: { + result: any + } +}; + +function createError ({ id, error: { code, message } }: ErrorDef) { + return { + id, + jsonrpc: '2.0', + error: { + code, + message + } + }; +} + +function createReply ({ id, reply: { result } }: ReplyDef) { + return { + id, + jsonrpc: '2.0', + result + }; +} + +function mockWs (requests: Array<{ method: string }>) { + server = new Server(TEST_WS_URL); + + let requestCount = 0; + const scope: { body: { [index: string]: {} }, requests: number, server: Server, done: any } = { + body: {}, + requests: 0, + server, + done: () => + server.stop(() => { + // ignore + }) + }; + + server.on('connection', (socket) => { + // @ts-ignore definitions are wrong, this is 'on', not 'onmessage' + socket.on('message', (body: {}) => { + const request = requests[requestCount]; + // @ts-ignore Yes, SHOULD be fixed, this is a mess + const response = request.error + // @ts-ignore Yes, SHOULD be fixed, this is a mess + ? createError(request) + // @ts-ignore Yes, SHOULD be fixed, this is a mess + : createReply(request); + + scope.body[request.method] = body; + requestCount++; + + socket.send(JSON.stringify(response)); + }); + }); + + return scope; +} + +export { + TEST_WS_URL, + mockWs +}; diff --git a/packages/rpc-rx/LICENSE b/packages/rpc-rx/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/rpc-rx/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/rpc-rx/README.md b/packages/rpc-rx/README.md new file mode 100644 index 0000000000..23ed7c4d4c --- /dev/null +++ b/packages/rpc-rx/README.md @@ -0,0 +1,7 @@ +# @polkadot/rpc-rx + +An RxJs wrapper around the [@polkadot/rpc-core](../rpc-core). + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/rpc-rx/package.json b/packages/rpc-rx/package.json new file mode 100644 index 0000000000..ed185750d2 --- /dev/null +++ b/packages/rpc-rx/package.json @@ -0,0 +1,40 @@ +{ + "name": "@plugnet/rpc-rx", + "version": "0.76.1", + "description": "An RxJs wrapper around the Polkadot JS API", + "main": "index.js", + "keywords": [ + "Polkadot", + "RxJs" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-rx#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/rpc-core": "^0.76.1", + "@plugnet/rpc-provider": "^0.76.1", + "@types/memoizee": "^0.4.2", + "@types/rx": "^4.1.1", + "memoizee": "^0.4.14", + "rxjs": "^6.4.0" + } +} diff --git a/packages/rpc-rx/src/cached.spec.ts b/packages/rpc-rx/src/cached.spec.ts new file mode 100644 index 0000000000..1cc8402623 --- /dev/null +++ b/packages/rpc-rx/src/cached.spec.ts @@ -0,0 +1,88 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import testingPairs from '@plugnet/keyring/testingPairs'; +import { RpcInterface$Section } from '@plugnet/rpc-core/types'; +import { AccountId } from '@plugnet/types'; + +jest.mock('@polkadot/rpc-provider/ws', () => class { + isConnected = () => true; + on = () => true; + send = () => true; +}); + +import RpcRx from '.'; + +describe('createCachedObservable', () => { + let api: RpcRx; + let creator: (...params: Array) => Observable; + const keyring = testingPairs(); + let section: RpcInterface$Section; + + beforeEach(() => { + api = new RpcRx(); + }); + + beforeEach(() => { + // Create two methods in our section + const subMethod: any = jest.fn(() => Promise.resolve('subMethodResult')); + subMethod.unsubscribe = jest.fn(() => Promise.resolve(true)); + const subMethod2: any = jest.fn(() => Promise.resolve('subMethod2Result')); + subMethod2.unsubscribe = jest.fn(() => Promise.resolve(true)); + + section = { + subMethod, + subMethod2 + }; + + // @ts-ignore Private method + creator = api.createObservable('subMethod', section); + }); + + it('creates a single observable', () => { + creator(123).subscribe(); + + expect( + section.subMethod + ).toHaveBeenCalledWith(123, expect.anything()); + }); + + it('creates a single observable (multiple calls)', () => { + const observable1 = creator(123); + const observable2 = creator(123); + + expect( + observable2 + ).toBe(observable1); + }); + + it('creates a single observable (multiple calls, different arguments that should be cached together)', () => { + const observable1 = creator(keyring.alice.address()); + const observable2 = creator(new AccountId(keyring.alice.address())); + + expect( + observable2 + ).toBe(observable1); + }); + + it('creates multiple observables for different values', () => { + const observable1 = creator(123); + const observable2 = creator(456); + + expect( + observable2 + ).not.toBe(observable1); + }); + + it('creates different observables for different methods but same arguments', () => { + const observable1 = creator(123); + // @ts-ignore Private method + const observable2 = api.createObservable('subMethod2', section)(123); + + expect( + observable2 + ).not.toBe(observable1); + }); +}); diff --git a/packages/rpc-rx/src/index.spec.ts b/packages/rpc-rx/src/index.spec.ts new file mode 100644 index 0000000000..e2fe6eedee --- /dev/null +++ b/packages/rpc-rx/src/index.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isFunction } from '@plugnet/util'; + +jest.mock('@polkadot/rpc-provider/ws', () => class { + isConnected = () => true; + on = () => true; + send = () => true; +}); + +import RpcRx from '.'; + +describe('RpcRx', () => { + let api: RpcRx; + + beforeEach(() => { + api = new RpcRx(); + }); + + it('creates an instance with all sections', () => { + expect( + Object + .keys(api) + .filter((key) => + !key.startsWith('_') + ) + ).toEqual([ + 'author', 'chain', 'state', 'system' + ]); + }); + + it('has isConnected', () => { + expect( + isFunction(api.isConnected) + ).toBe(true); + }); +}); diff --git a/packages/rpc-rx/src/index.ts b/packages/rpc-rx/src/index.ts new file mode 100644 index 0000000000..01ec33533c --- /dev/null +++ b/packages/rpc-rx/src/index.ts @@ -0,0 +1,184 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcInterface$Method, RpcInterface$Section } from '@plugnet/rpc-core/types'; +import { ProviderInterface } from '@plugnet/rpc-provider/types'; +import { RpcRxInterface, RpcRxInterface$Events, RpcRxInterface$Method, RpcRxInterface$Section } from './types'; + +import EventEmitter from 'eventemitter3'; +import memoize, { Memoized } from 'memoizee'; +import { BehaviorSubject, Observable, from, Observer } from 'rxjs'; +import Rpc from '@plugnet/rpc-core'; +import { map, publishReplay, refCount } from 'rxjs/operators'; +import { isFunction, isUndefined } from '@plugnet/util'; + +/** + * @name RpcRx + * @summary The RxJS API is a wrapper around the API. + * @description It allows wrapping API components with observables using RxJS. + * + * @example + *
+ * + * ```javascript + * import RpcRx from '@plugnet/rpc-rx'; + * import WsProvider from '@plugnet/rpc-provider/ws'; + * + * const provider = new WsProvider('http://127.0.0.1:9944'); + * const api = new RpcRx(provider); + * ``` + */ +export default class RpcRx implements RpcRxInterface { + private _api: Rpc; + private _eventemitter: EventEmitter; + private _isConnected: BehaviorSubject; + readonly author: RpcRxInterface$Section; + readonly chain: RpcRxInterface$Section; + readonly state: RpcRxInterface$Section; + readonly system: RpcRxInterface$Section; + + /** + * @param {ProviderInterface} provider An API provider using HTTP or WebSocket + */ + constructor (providerOrRpc?: Rpc | ProviderInterface) { + this._api = providerOrRpc instanceof Rpc + ? providerOrRpc + : new Rpc(providerOrRpc); + this._eventemitter = new EventEmitter(); + this._isConnected = new BehaviorSubject(this._api._provider.isConnected()); + + this.initEmitters(this._api._provider); + + this.author = this.createInterface(this._api.author); + this.chain = this.createInterface(this._api.chain); + this.state = this.createInterface(this._api.state); + this.system = this.createInterface(this._api.system); + } + + isConnected (): BehaviorSubject { + return this._isConnected; + } + + on (type: RpcRxInterface$Events, handler: (...args: Array) => any): void { + this._eventemitter.on(type, handler); + } + + protected emit (type: RpcRxInterface$Events, ...args: Array): void { + this._eventemitter.emit(type, ...args); + } + + private initEmitters (provider: ProviderInterface): void { + provider.on('connected', () => { + this._isConnected.next(true); + + this.emit('connected'); + }); + + provider.on('disconnected', () => { + this._isConnected.next(false); + + this.emit('disconnected'); + }); + } + + private createInterface (section: RpcInterface$Section): RpcRxInterface$Section { + return Object + .keys(section) + .filter((name) => !['subscribe', 'unsubscribe'].includes(name)) + .reduce((observables, name) => { + observables[name] = this.createObservable(name, section); + + return observables; + }, ({} as RpcRxInterface$Section)); + } + + private createObservable (name: string, section: RpcInterface$Section): RpcRxInterface$Method { + if (isFunction(section[name].unsubscribe)) { + const memoized: Memoized = memoize( + (...params: Array) => this.createReplay(name, params, section, memoized), + { + length: false, + // Normalize args so that different args that should be cached + // together are cached together. + // E.g.: `query.my.method('abc') === query.my.method(new AccountId('abc'));` + normalizer: (args) => { + // `args` is arguments object as accessible in memoized function + return JSON.stringify(args); + } + } + ); + + return memoized as unknown as RpcRxInterface$Method; + } + + // We voluntarily don't cache the "one-shot" RPC calls. For example, + // `getStorage('123')` returns the current value, but this value can change + // over time, so we wouldn't want to cache the Observable. + return (...params: Array): Observable => + from( + section[name] + .apply(null, params) + .catch((error: Error) => { + console.error(error); + }) + ); + } + + private createReplay (name: string, params: Array, section: RpcInterface$Section, memoized: Memoized): Observable { + return new Observable((observer: Observer): Function => { + const fn = section[name]; + const callback = this.createReplayCallback(observer); + const subscribe = fn(...params, callback).catch((error) => + observer.next(error) + ); + + return this.createReplayUnsub(fn, subscribe, params, memoized); + }) + .pipe( + map((value) => { + if (value instanceof Error) { + throw value; + } + + return value; + }), + publishReplay(1), + refCount() + ); + } + + private createReplayCallback (observer: Observer) { + let cachedResult: any; + + return (result: any) => { + if (isUndefined(cachedResult) || !Array.isArray(cachedResult) || !Array.isArray(result) + || result.length !== cachedResult.length) { + cachedResult = result; + } else { + cachedResult = cachedResult.map((cachedValue, index) => + isUndefined(result[index]) + ? cachedValue + : result[index] + ); + } + + observer.next(cachedResult); + }; + } + + private createReplayUnsub (fn: RpcInterface$Method, subscribe: Promise, params: Array, memoized: Memoized): () => void { + return (): void => { + subscribe + .then((subscriptionId: number) => + fn.unsubscribe(subscriptionId) + ) + .then(() => { + memoized.delete(...params); + }) + .catch((error) => { + console.error('Unsubscribe failed', error); + }); + }; + } +} diff --git a/packages/rpc-rx/src/replay.spec.ts b/packages/rpc-rx/src/replay.spec.ts new file mode 100644 index 0000000000..5a43027096 --- /dev/null +++ b/packages/rpc-rx/src/replay.spec.ts @@ -0,0 +1,84 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { RpcInterface$Section } from '@plugnet/rpc-core/types'; + +jest.mock('@polkadot/rpc-provider/ws', () => class { + isConnected = () => true; + on = () => true; + send = () => true; +}); + +import RpcRx from '.'; + +describe('replay', () => { + const params = [123, false]; + let api: RpcRx; + let section: RpcInterface$Section; + let observable: Observable; + let update: any; + + beforeEach(() => { + api = new RpcRx(); + }); + + beforeEach(() => { + const subMethod: any = jest.fn((name, ...params) => { + update = params.pop(); + + return Promise.resolve(12345); + }); + + subMethod.unsubscribe = jest.fn(() => { + return Promise.resolve(true); + }); + + section = { + subMethod + }; + + // @ts-ignore + observable = api.createObservable('subMethod', section)(...params); + }); + + it('subscribes via the api section', (done) => { + observable.subscribe((value) => { + if (value) { + expect( + section.subMethod + ).toHaveBeenCalledWith(123, false, expect.anything()); + + done(); + } + }); + + update('test'); + }); + + it('returns the observable value', (done) => { + observable.subscribe((value) => { + if (value) { + expect(value).toEqual('test'); + done(); + } + }); + + update('test'); + }); + + it('unsubscribes as required', (done) => { + section.subMethod.unsubscribe = async () => { + done(); + }; + + let subscription = observable.subscribe((value) => { + if (value) { + subscription.unsubscribe(); + } + }); + + update('test'); + }); +}); diff --git a/packages/rpc-rx/src/types.ts b/packages/rpc-rx/src/types.ts new file mode 100644 index 0000000000..7f1f2e609e --- /dev/null +++ b/packages/rpc-rx/src/types.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Observable } from 'rxjs'; +import { ProviderInterface$Emitted } from '@plugnet/rpc-provider/types'; + +export type RpcRxInterface$Method = (...params: Array) => Observable; + +export type RpcRxInterface$Section = { + [index: string]: RpcRxInterface$Method +}; + +export type RpcRxInterface$Events = ProviderInterface$Emitted; + +export type RpcRxInterface = { + readonly author: RpcRxInterface$Section; + readonly chain: RpcRxInterface$Section; + readonly state: RpcRxInterface$Section; + readonly system: RpcRxInterface$Section; + + isConnected: () => Observable + on: (type: RpcRxInterface$Events, handler: (...args: Array) => any) => void; +}; diff --git a/packages/rpc-rx/test/e2e/subscribe.test.js b/packages/rpc-rx/test/e2e/subscribe.test.js new file mode 100644 index 0000000000..abb0a8fdb5 --- /dev/null +++ b/packages/rpc-rx/test/e2e/subscribe.test.js @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/rpc-rx authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Ws from '@polkadot/rpc-provider/ws'; +import storage from '@polkadot/storage/static'; + +import RpcRx from '../../src'; + +describe.skip('e2e subscriptions', () => { + let api; + + beforeEach(() => { + jest.setTimeout(30000); + + api = new RpcRx(new Ws('ws://127.0.0.1:9944')); + }); + + it('retrieves current timestamp', (done) => { + let count = 0; + + api.state + .subscribeStorage([[storage.timestamp.now]]) + .subscribe((data) => { + console.error('timestamp.now', data); + + if (++count === 4) { + done(); + } + }); + }); + + it('retrieves multiple values', (done) => { + api.state + .subscribeStorage([ + [storage.timestamp.now], + [storage.session.sessionLength], + [storage.staking.sessionsPerEra] + ]) + .subscribe((data) => { + console.error('multiples', data); + + if (data !== undefined && data.filter((item) => item).length === 3) { + done(); + } + }); + }); +}); diff --git a/packages/type-extrinsics/LICENSE b/packages/type-extrinsics/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/type-extrinsics/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/type-extrinsics/README.md b/packages/type-extrinsics/README.md new file mode 100644 index 0000000000..8b7156f966 --- /dev/null +++ b/packages/type-extrinsics/README.md @@ -0,0 +1,7 @@ +# @polkadot/extrinsics + +Base extrinsic definitions & codecs + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/type-extrinsics/package.json b/packages/type-extrinsics/package.json new file mode 100644 index 0000000000..a6519f118b --- /dev/null +++ b/packages/type-extrinsics/package.json @@ -0,0 +1,17 @@ +{ + "name": "@plugnet/extrinsics", + "version": "0.76.1", + "main": "index.js", + "repository": "https://github.com/polkadot-js/api.git", + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/types": "^0.76.1", + "@plugnet/util": "^0.76.1" + } +} diff --git a/packages/type-extrinsics/src/fromMetadata.spec.ts b/packages/type-extrinsics/src/fromMetadata.spec.ts new file mode 100644 index 0000000000..d3c40c3a80 --- /dev/null +++ b/packages/type-extrinsics/src/fromMetadata.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Extrinsic, Metadata } from '@plugnet/types'; +import json from '@plugnet/types/Metadata/v0/static'; + +import fromMetadata from './fromMetadata'; + +// Use the pre-generated metadata +const metadata = new Metadata(json).asV0; +const newExtrinsics = fromMetadata(metadata); + +describe('fromMetadata', () => { + it('should throw if an incorrect number of args is supplied', () => { + expect(() => newExtrinsics.balances.setBalance()).toThrowError(/expects 3 arguments/); + }); + + it('should return a value if the storage function does not expect an argument', () => { + expect(() => newExtrinsics.balances.setBalance('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF', 2, 3)).not.toThrow(); + }); + + it('should return properly-encoded transactions', () => { + expect( + new Extrinsic(newExtrinsics.timestamp.set([10101])).toU8a() + ).toEqual( + new Uint8Array([ + // length (encoded) + 4 << 2, + // version, no signature + 1, + // index + 0, 0, + // values, Compact + 116 + ]) + ); + }); +}); diff --git a/packages/type-extrinsics/src/fromMetadata.ts b/packages/type-extrinsics/src/fromMetadata.ts new file mode 100644 index 0000000000..2723a95201 --- /dev/null +++ b/packages/type-extrinsics/src/fromMetadata.ts @@ -0,0 +1,58 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RuntimeModuleMetadata } from '@plugnet/types/Metadata/v0/Modules'; +import { Methods, ModulesWithMethods } from '@plugnet/types/primitive/Method'; +import MetadataV0 from '@plugnet/types/Metadata/v0'; + +import { stringCamelCase } from '@plugnet/util'; + +import createUnchecked from './utils/createUnchecked'; +import extrinsics from '.'; + +/** + * Extend a storage object with the storage modules & module functions present + * in the metadata. + * + * @param extrinsics - An extrinsics object to be extended. + * @param metadata - The metadata to extend the storage object against. + */ +export default function fromMetadata (metadata: MetadataV0): ModulesWithMethods { + let indexCount = -1; + + const findIndex = (prefix: string): number => { + indexCount++; + + const mod = metadata.calls.find((item) => item.prefix.toString() === prefix); + + if (!mod) { + console.error(`Unable to find module index for '${prefix}'`); + + // compatible with old versions + return indexCount; + } + + return mod.index.toNumber(); + }; + + return metadata.modules.reduce((result, meta: RuntimeModuleMetadata) => { + if (!meta.module.call || !meta.module.call.functions.length) { + return result; + } + + const prefix = stringCamelCase(meta.prefix.toString()); + const index = findIndex(meta.prefix.toString()); + + result[prefix] = meta.module.call.functions.reduce((newModule, funcMeta) => { + // extrinsics.balances.set_balance -> extrinsics.balances.setBalance + const funcName = stringCamelCase(funcMeta.name.toString()); + + newModule[funcName] = createUnchecked(prefix, funcName, index, funcMeta); + + return newModule; + }, {} as Methods); + + return result; + }, { ...extrinsics }); +} diff --git a/packages/type-extrinsics/src/index.spec.ts b/packages/type-extrinsics/src/index.spec.ts new file mode 100644 index 0000000000..8cf6a2e51c --- /dev/null +++ b/packages/type-extrinsics/src/index.spec.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import testingPairs from '@plugnet/keyring/testingPairs'; +import { Extrinsic } from '@plugnet/types'; + +import extrinsics from './static'; + +const keyring = testingPairs({ type: 'ed25519' }, false); + +describe('extrinsics', () => { + it('encodes extrinsic correctly (nobody)', () => { + expect( + new Extrinsic( + extrinsics.timestamp.set(10101) + ).sign( + keyring.nobody, + { + blockHash: new Uint8Array(), + nonce: 1234 + } + ).toU8a(true) + ).toEqual(new Uint8Array([ + 129, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 19, 0, 0, 0, 213, 157 + ])); + }); + + it('encodes an actual transfer (actual data)', () => { + expect( + new Extrinsic( + extrinsics.balances.transfer(keyring.bob.publicKey(), 6969) + ).sign(keyring.alice, { + blockHash: '0xec7afaf1cca720ce88c1d1b689d81f0583cc15a97d621cf046dd9abf605ef22f', + nonce: 0 + }).toU8a() + ).toEqual(new Uint8Array([ + 37, 2, 129, 255, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79, 245, 228, 136, 9, 232, 31, 206, 95, 236, 20, 206, 189, 109, 251, 83, 198, 37, 79, 144, 90, 72, 33, 101, 250, 52, 0, 66, 80, 222, 78, 34, 124, 6, 101, 244, 44, 168, 225, 239, 153, 185, 58, 113, 221, 221, 136, 43, 237, 9, 6, 201, 17, 56, 110, 105, 78, 173, 109, 176, 100, 24, 78, 164, 15, 0, 0, 3, 0, 255, 215, 86, 142, 95, 10, 126, 218, 103, 168, 38, 145, 255, 55, 154, 196, 187, 164, 249, 201, 184, 89, 254, 119, 155, 93, 70, 54, 59, 97, 173, 45, 185, 229, 108 + ])); + }); +}); diff --git a/packages/type-extrinsics/src/index.ts b/packages/type-extrinsics/src/index.ts new file mode 100644 index 0000000000..f27ceb7bab --- /dev/null +++ b/packages/type-extrinsics/src/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ModulesWithMethods } from '@plugnet/types/primitive/Method'; + +const extrinsics: ModulesWithMethods = {}; + +export default extrinsics; diff --git a/packages/type-extrinsics/src/static.ts b/packages/type-extrinsics/src/static.ts new file mode 100644 index 0000000000..23889bf286 --- /dev/null +++ b/packages/type-extrinsics/src/static.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Metadata } from '@plugnet/types'; +import metadataRpc from '@plugnet/types/Metadata/v0/static'; +import { ModulesWithMethods } from '@plugnet/types/primitive/Method'; + +import fromMetadata from './fromMetadata'; + +const staticMetadata: ModulesWithMethods = fromMetadata( + new Metadata(metadataRpc).asV0 +); +export default staticMetadata; diff --git a/packages/type-extrinsics/src/utils/createUnchecked.ts b/packages/type-extrinsics/src/utils/createUnchecked.ts new file mode 100644 index 0000000000..d6888b09c9 --- /dev/null +++ b/packages/type-extrinsics/src/utils/createUnchecked.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { FunctionMetadata } from '@plugnet/types/Metadata/v0/Modules'; +import { MethodFunction } from '@plugnet/types/primitive/Method'; +import { Method } from '@plugnet/types'; +import { assert } from '@plugnet/util'; + +/** + * From the metadata of a function in the module's storage, generate the function + * that will return the an UncheckExtrinsic. + * + * @param index - Index of the module section in the modules array. + */ +export default function createDescriptor ( + section: string, + method: string, + index: number, + meta: FunctionMetadata +): MethodFunction { + const callIndex = new Uint8Array([index, meta.id.toNumber()]); + let extrinsicFn: any; + + const expectedArgs = Method.filterOrigin(meta); + + extrinsicFn = (...args: any[]): Method => { + assert(expectedArgs.length.valueOf() === args.length, `Extrinsic ${section}.${method} expects ${expectedArgs.length.valueOf()} arguments, got ${args.length}.`); + + return new Method({ + args, + callIndex + }, meta); + }; + + extrinsicFn.callIndex = callIndex; + extrinsicFn.meta = meta; + extrinsicFn.method = method; + extrinsicFn.section = section; + extrinsicFn.toJSON = (): any => + meta.toJSON(); + + return extrinsicFn as MethodFunction; +} diff --git a/packages/type-extrinsics/test/e2e/transfer.spec.js b/packages/type-extrinsics/test/e2e/transfer.spec.js new file mode 100644 index 0000000000..ccc456f208 --- /dev/null +++ b/packages/type-extrinsics/test/e2e/transfer.spec.js @@ -0,0 +1,58 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Rpc from '@polkadot/rpc-core'; +import Ws from '@polkadot/rpc-provider/ws'; +import testingPairs from '@polkadot/keyring/testingPairs'; + +import extrinsics from '../../src/static'; + +const keyring = testingPairs({ type: 'ed25519' }); + +describe.skip('e2e transfer', () => { + let api; + + beforeAll(() => { + api = new Rpc(new Ws('ws://127.0.0.1:9944')); + }); + + // Error: [1002]: Inherent transactions cannot be queued. + it.skip('inherent test', () => { + const inherent = extrinsics.timestamp.set(Math.round(Date.now() / 1000)); + + return api.author.submitExtrinsic(inherent.toU8a()).then(console.log); + }); + + it.skip('makes a transfer for a transaction', () => { + return api.chain + .getBlockHash(0) + .then((genesisHash) => { + const extrinsic = extrinsics.balances.transfer(keyring.bob.publicKey(), 6969); + + extrinsic.sign(keyring.alice, { blockHash: genesisHash, nonce: 0 }); + + return api.author.submitExtrinsic(extrinsic.toU8a()); + }) + .then(console.log); + }); + + it('makes a transfer via watch', (done) => { + return api.chain + .getBlockHash(0) + .then((genesisHash) => { + const extrinsic = extrinsics.balances.transfer(keyring.bob.publicKey(), 6969); + + extrinsic.sign(keyring.alice, { blockHash: genesisHash, nonce: 0 }); + + return api.author.submitAndWatchExtrinsic(extrinsic, (result) => { + console.log(result); + + if (result.status.isFinalized) { + done(); + } + }); + }) + .then(console.log); + }); +}); diff --git a/packages/type-jsonrpc/LICENSE b/packages/type-jsonrpc/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/type-jsonrpc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/type-jsonrpc/README.md b/packages/type-jsonrpc/README.md new file mode 100644 index 0000000000..950a9beae2 --- /dev/null +++ b/packages/type-jsonrpc/README.md @@ -0,0 +1,24 @@ +# @polkadot/jsonrpc + +A definition of all the methods exposed in a general Polkadot client application. These are used not only to provide a comprehensive code-generated document of the available methods, but are also used in the API to auto-generate endpoints with the required type-checking. + +For a list of currently exposed methods, see the [method documentation](docs/METHODS_RPC.md). + +## Usage + +Installation - + +``` +yarn add @polkadot/jsonrpc +``` + +## Adding methods + +As methods are added, simply adding the name, inputs & output will prepare it for use. + +- Add the method to the correct file in [src/rpc/](src/rpc/) (Input/Output types as cross-referenced from the canonical implementation and match one-to-one) +- Should a new type be required, add it to the type list, [src/types.js](src/types.js) (Required for TSLint type checking) + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/type-jsonrpc/package.json b/packages/type-jsonrpc/package.json new file mode 100644 index 0000000000..135b093dc2 --- /dev/null +++ b/packages/type-jsonrpc/package.json @@ -0,0 +1,34 @@ +{ + "name": "@plugnet/jsonrpc", + "version": "0.76.1", + "description": "Method definitions for the Polkadot RPC layer", + "main": "index.js", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "keywords": [ + "Polkadot", + "JsonRPC" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/type-jsonrpc#readme", + "dependencies": { + "@babel/runtime": "^7.4.3" + } +} diff --git a/packages/type-jsonrpc/src/author.ts b/packages/type-jsonrpc/src/author.ts new file mode 100644 index 0000000000..54d8a0f645 --- /dev/null +++ b/packages/type-jsonrpc/src/author.ts @@ -0,0 +1,54 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcMethodOpt, RpcSection } from './types'; + +import createMethod from './create/method'; +import createParam from './create/param'; + +const pendingExtrinsics: RpcMethodOpt = { + description: 'Returns all pending extrinsics, potentially grouped by sender', + params: [], + type: 'PendingExtrinsics' +}; + +const submitExtrinsic: RpcMethodOpt = { + isSigned: true, + description: 'Submit a fully formatted extrinsic for block inclusion', + params: [ + createParam('extrinsic', 'Extrinsic') + ], + type: 'Hash' +}; + +const submitAndWatchExtrinsic: RpcMethodOpt = { + description: 'Subscribe and watch an extrinsic until unsubscribed', + isSigned: true, + params: [ + createParam('extrinsic', 'Extrinsic') + ], + pubsub: [ + 'extrinsicUpdate', + 'submitAndWatchExtrinsic', + 'unwatchExtrinsic' + ], + type: 'ExtrinsicStatus' +}; + +const section = 'author'; + +/** + * @summary Methods to work with authors & contributors. + */ +export default { + isDeprecated: false, + isHidden: false, + description: 'Authoring of network items', + section, + methods: { + pendingExtrinsics: createMethod(section, 'pendingExtrinsics', pendingExtrinsics), + submitExtrinsic: createMethod(section, 'submitExtrinsic', submitExtrinsic), + submitAndWatchExtrinsic: createMethod(section, 'submitAndWatchExtrinsic', submitAndWatchExtrinsic) + } +} as RpcSection; diff --git a/packages/type-jsonrpc/src/chain.ts b/packages/type-jsonrpc/src/chain.ts new file mode 100644 index 0000000000..ef7570f926 --- /dev/null +++ b/packages/type-jsonrpc/src/chain.ts @@ -0,0 +1,106 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcMethodOpt, RpcSection } from './types'; + +import createMethod from './create/method'; +import createParam from './create/param'; + +// NOTE Order is the same as in Rust + +const getHeader: RpcMethodOpt = { + description: 'Retrieves the header for a specific block', + params: [ + createParam('hash', 'Hash', { isOptional: true }) + ], + type: 'Header' +}; + +const getBlock: RpcMethodOpt = { + description: 'Get header and body of a relay chain block', + params: [ + createParam('hash', 'Hash', { isOptional: true }) + ], + type: 'SignedBlock' +}; + +const getBlockHash: RpcMethodOpt = { + description: 'Get the block hash for a specific block', + params: [ + createParam('blockNumber', 'BlockNumber', { isOptional: true }) + ], + type: 'Hash' +}; + +const getFinalizedHead: RpcMethodOpt = { + description: 'Get hash of the last finalised block in the canon chain', + params: [], + type: 'Hash' +}; + +const getRuntimeVersion: RpcMethodOpt = { + description: 'Get the runtime version (alias of state_getRuntimeVersion)', + params: [ + createParam('hash', 'Hash', { isOptional: true }) + ], + type: 'RuntimeVersion' +}; + +const subscribeNewHead: RpcMethodOpt = { + description: 'Retrieves the best header via subscription', + params: [], + pubsub: [ + 'newHead', + 'subscribeNewHead', + 'unsubscribeNewHead' + ], + type: 'Header' +}; + +const subscribeFinalizedHeads: RpcMethodOpt = { + description: 'Retrieves the best finalized header via subscription', + params: [], + pubsub: [ + 'finalizedHead', + // TODO US spelling + 'subscribeFinalisedHeads', + 'unsubscribeFinalisedHeads' + ], + type: 'Header' +}; + +const subscribeRuntimeVersion: RpcMethodOpt = { + description: 'Retrieves the runtime version via subscription', + params: [], + pubsub: [ + 'runtimeVersion', + 'subscribeRuntimeVersion', + 'unsubscribeRuntimeVersion' + ], + type: 'RuntimeVersion' +}; + +const section = 'chain'; + +/** + * @summary Methods to retrieve chain data. + */ +export default { + isDeprecated: false, + isHidden: false, + description: 'Retrieval of chain data', + section, + methods: { + getBlock: createMethod(section, 'getBlock', getBlock), + getBlockHash: createMethod(section, 'getBlockHash', getBlockHash), + // TODO US spelling + getFinalizedHead: createMethod(section, 'getFinalisedHead', getFinalizedHead), + getHeader: createMethod(section, 'getHeader', getHeader), + getRuntimeVersion: createMethod(section, 'getRuntimeVersion', getRuntimeVersion), + // TODO US spelling + subscribeFinalizedHeads: createMethod(section, 'subscribeFinalisedHeads', subscribeFinalizedHeads), + subscribeRuntimeVersion: createMethod(section, 'subscribeRuntimeVersion', subscribeRuntimeVersion), + subscribeNewHead: createMethod(section, 'subscribeNewHead', subscribeNewHead) + } +} as RpcSection; diff --git a/packages/type-jsonrpc/src/create/method.ts b/packages/type-jsonrpc/src/create/method.ts new file mode 100644 index 0000000000..7ad8166f7c --- /dev/null +++ b/packages/type-jsonrpc/src/create/method.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcMethodOpt, RpcMethod } from '../types'; + +import { isUndefined } from '@plugnet/util'; + +export default function createMethod (section: string, method: string, { description, isDeprecated = false, isHidden = false, isSigned = false, params, pubsub, type }: RpcMethodOpt): RpcMethod { + return { + description, + isDeprecated, + isHidden, + isSigned, + isSubscription: !isUndefined(pubsub), + method, + params, + pubsub: pubsub || ['', '', ''], + section, + type + }; +} diff --git a/packages/type-jsonrpc/src/create/param.ts b/packages/type-jsonrpc/src/create/param.ts new file mode 100644 index 0000000000..e58832fd04 --- /dev/null +++ b/packages/type-jsonrpc/src/create/param.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { CodecTypes } from '@plugnet/types/classes'; +import { RpcParam } from '../types'; + +type RpcParamOptions = { + isOptional?: boolean +}; + +export default function createParam (name: string, type: CodecTypes, { isOptional = false }: RpcParamOptions = { isOptional: false }): RpcParam { + return { + isOptional, + name, + type + }; +} diff --git a/packages/type-jsonrpc/src/index.spec.js b/packages/type-jsonrpc/src/index.spec.js new file mode 100644 index 0000000000..3a0e22ad77 --- /dev/null +++ b/packages/type-jsonrpc/src/index.spec.js @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import interfaces from '.'; + +describe('jsonrpc', () => { + it('exports the available interfaces', () => { + expect(interfaces).toBeDefined(); + expect(Object.keys(interfaces).length).toBeGreaterThan(0); + }); +}); diff --git a/packages/type-jsonrpc/src/index.ts b/packages/type-jsonrpc/src/index.ts new file mode 100644 index 0000000000..bb9d1ad1a5 --- /dev/null +++ b/packages/type-jsonrpc/src/index.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcSection } from './types'; + +import author from './author'; +import chain from './chain'; +import state from './state'; +import system from './system'; + +const interfaces: { [key: string]: RpcSection } = { + author, + chain, + state, + system +}; + +export default interfaces; diff --git a/packages/type-jsonrpc/src/state.ts b/packages/type-jsonrpc/src/state.ts new file mode 100644 index 0000000000..166a8cc77b --- /dev/null +++ b/packages/type-jsonrpc/src/state.ts @@ -0,0 +1,111 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcMethodOpt, RpcSection } from './types'; + +import createMethod from './create/method'; +import createParam from './create/param'; + +// NOTE Order here is the same as in the Rust code + +const call: RpcMethodOpt = { + description: 'Perform a call to a builtin on the chain', + params: [ + createParam('method', 'Text'), + createParam('data', 'Bytes'), + createParam('block', 'Hash', { isOptional: true }) + ], + type: 'Bytes' +}; + +const getStorage: RpcMethodOpt = { + description: 'Retrieves the storage for a key', + params: [ + createParam('key', 'StorageKey'), + createParam('block', 'Hash', { isOptional: true }) + ], + type: 'StorageData' +}; + +const getStorageHash: RpcMethodOpt = { + description: 'Retrieves the storage hash', + params: [ + createParam('key', 'StorageKey'), + createParam('block', 'Hash', { isOptional: true }) + ], + type: 'Hash' +}; + +const getStorageSize: RpcMethodOpt = { + description: 'Retrieves the storage size', + params: [ + createParam('key', 'StorageKey'), + createParam('block', 'Hash', { isOptional: true }) + ], + type: 'u64' +}; + +const getMetadata: RpcMethodOpt = { + description: 'Returns the runtime metadata', + params: [ + createParam('block', 'Hash', { isOptional: true }) + ], + type: 'Metadata' +}; + +const getRuntimeVersion: RpcMethodOpt = { + description: 'Get the runtime version', + params: [ + createParam('hash', 'Hash', { isOptional: true }) + ], + type: 'RuntimeVersion' +}; + +const queryStorage: RpcMethodOpt = { + description: 'Query historical storage entries (by key) starting from a start block', + params: [ + // @ts-ignore The Vec<> wrap is fine + createParam('keys', 'Vec'), + createParam('startBlock', 'Hash'), + createParam('block', 'Hash', { isOptional: true }) + ], + // @ts-ignore The Vec<> wrap is fine + type: 'Vec' +}; + +const subscribeStorage: RpcMethodOpt = { + description: 'Subscribes to storage changes for the provided keys', + params: [ + // @ts-ignore The Vec<> wrap is fine + createParam('keys', 'Vec') + ], + pubsub: [ + 'storage', + 'subscribeStorage', + 'unsubscribeStorage' + ], + type: 'StorageChangeSet' +}; + +const section = 'state'; + +/** + * @summary Query the state and state storage. + */ +export default { + isDeprecated: false, + isHidden: false, + description: 'Query of state', + section, + methods: { + call: createMethod(section, 'call', call), + getMetadata: createMethod(section, 'getMetadata', getMetadata), + getRuntimeVersion: createMethod(section, 'getRuntimeVersion', getRuntimeVersion), + getStorage: createMethod(section, 'getStorage', getStorage), + getStorageHash: createMethod(section, 'getStorageHash', getStorageHash), + getStorageSize: createMethod(section, 'getStorageSize', getStorageSize), + queryStorage: createMethod(section, 'queryStorage', queryStorage), + subscribeStorage: createMethod(section, 'subscribeStorage', subscribeStorage) + } +} as RpcSection; diff --git a/packages/type-jsonrpc/src/system.ts b/packages/type-jsonrpc/src/system.ts new file mode 100644 index 0000000000..8dc73e3f58 --- /dev/null +++ b/packages/type-jsonrpc/src/system.ts @@ -0,0 +1,73 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { RpcMethodOpt, RpcSection } from './types'; + +import createMethod from './create/method'; + +// NOTE order here is the same as in Rust, alphabetical below + +const name: RpcMethodOpt = { + description: 'Retrieves the node name', + params: [], + type: 'Text' +}; + +const version: RpcMethodOpt = { + description: 'Retrieves the version of the node', + params: [], + type: 'Text' +}; + +const chain: RpcMethodOpt = { + description: 'Retrieves the chain', + params: [], + type: 'Text' +}; + +const properties: RpcMethodOpt = { + description: 'Get a custom set of properties as a JSON object, defined in the chain spec', + params: [], + type: 'ChainProperties' +}; + +const health: RpcMethodOpt = { + description: 'Return health status of the node', + params: [], + type: 'Health' +}; + +const peers: RpcMethodOpt = { + description: 'Returns the currently connected peers', + params: [], + // @ts-ignore the Vec<> wrap is fine + type: 'Vec' +}; + +const networkState: RpcMethodOpt = { + description: 'Returns current state of the network', + params: [], + type: 'NetworkState' +}; + +const section = 'system'; + +/** + * @summary Methods to retrieve system info. + */ +export default { + isDeprecated: false, + isHidden: false, + description: 'Methods to retrieve system info', + section, + methods: { + chain: createMethod(section, 'chain', chain), + health: createMethod(section, 'health', health), + name: createMethod(section, 'name', name), + networkState: createMethod(section, 'networkState', networkState), + peers: createMethod(section, 'peers', peers), + properties: createMethod(section, 'properties', properties), + version: createMethod(section, 'version', version) + } +} as RpcSection; diff --git a/packages/type-jsonrpc/src/types.ts b/packages/type-jsonrpc/src/types.ts new file mode 100644 index 0000000000..cce5f347a0 --- /dev/null +++ b/packages/type-jsonrpc/src/types.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/jsonrpc authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { CodecTypes } from '@plugnet/types/classes'; + +type PubSub = [string, string, string]; + +export type RpcParam = { + isOptional: boolean, + name: string, + type: CodecTypes +}; + +export type RpcMethodOpt = { + description: string, + isDeprecated?: boolean, + isHidden?: boolean, + isSigned?: boolean, + isSubscription?: boolean, + params: Array, + pubsub?: PubSub, + type: CodecTypes +}; + +export type RpcMethod = { + alias?: string, + description: string, + isDeprecated: boolean, + isHidden: boolean, + isSigned: boolean, + isSubscription: boolean, + method: string, + params: Array, + pubsub: PubSub, + section: string, + type: CodecTypes +}; + +export type RpcSection = { + isDeprecated: boolean, + isHidden: boolean, + description: string, + section: string, + methods: { + [index: string]: RpcMethod + } +}; diff --git a/packages/type-storage/LICENSE b/packages/type-storage/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/type-storage/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/type-storage/README.md b/packages/type-storage/README.md new file mode 100644 index 0000000000..031d51ddc8 --- /dev/null +++ b/packages/type-storage/README.md @@ -0,0 +1,7 @@ +# @polkadot/storage + +An base interface to the storage layer + +## Classes + +[Classes](SUMMARY.md) diff --git a/packages/type-storage/package.json b/packages/type-storage/package.json new file mode 100644 index 0000000000..d6a09598ac --- /dev/null +++ b/packages/type-storage/package.json @@ -0,0 +1,38 @@ +{ + "name": "@plugnet/storage", + "version": "0.76.1", + "description": "Generic wrapper wrapper around state storage", + "main": "index.js", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "keywords": [ + "Polkadot", + "Merkel Patricia Tree" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/type-storage#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/keyring": "^0.76.1", + "@plugnet/types": "^0.76.1", + "@plugnet/util": "^0.76.1", + "@plugnet/util-crypto": "^0.76.1" + } +} diff --git a/packages/type-storage/src/fromMetadata.spec.ts b/packages/type-storage/src/fromMetadata.spec.ts new file mode 100644 index 0000000000..f49022eb52 --- /dev/null +++ b/packages/type-storage/src/fromMetadata.spec.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; +import { Metadata } from '@plugnet/types'; +import json from '@plugnet/types/Metadata/v0/static'; + +import fromMetadata from './fromMetadata'; + +// Use the pre-generated metadata +const metadata = new Metadata(json).asV0; +const newStorage = fromMetadata(metadata); + +describe('fromMetadata', () => { + it('should throw if the storage function expects an argument', () => { + expect(() => newStorage.balances.freeBalance()).toThrowError(/expects one argument/); + }); + + it('should return a value if the storage function does not expect an argument', () => { + expect(() => newStorage.timestamp.now()).not.toThrow(); + }); + + it('should return the correct length-prefixed storage key', () => { + expect( + u8aToHex( + newStorage.balances.freeBalance('5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo') + ) + ).toEqual( + '0x404af2c53fce3ec33c6ccccf22e926f1a7' + ); + }); +}); diff --git a/packages/type-storage/src/fromMetadata.ts b/packages/type-storage/src/fromMetadata.ts new file mode 100644 index 0000000000..7cc7e70e1e --- /dev/null +++ b/packages/type-storage/src/fromMetadata.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ModuleStorage, Storage } from './types'; + +import MetadataV0 from '@plugnet/types/Metadata/v0'; +import { stringLowerFirst } from '@plugnet/util'; + +import createFunction from './utils/createFunction'; +import storage from '.'; + +/** + * Extend a storage object with the storage modules & module functions present + * in the metadata. + * + * @param storage - A storage object to be extended. + * @param metadata - The metadata to extend the storage object against. + */ +export default function fromMetadata (metadata: MetadataV0): Storage { + return metadata.modules.reduce((result, moduleMetadata) => { + if (moduleMetadata.storage.isNone) { + return result; + } + + const prefix = moduleMetadata.storage.unwrap().prefix; + + // For access, we change the index names, i.e. Balances.FreeBalance -> balances.freeBalance + result[stringLowerFirst(prefix.toString())] = moduleMetadata.storage.unwrap().functions.reduce((newModule, func) => { + newModule[stringLowerFirst(func.name.toString())] = createFunction(prefix, func.name, func); + + return newModule; + }, {} as ModuleStorage); + + return result; + }, { ...storage }); +} diff --git a/packages/type-storage/src/index.ts b/packages/type-storage/src/index.ts new file mode 100644 index 0000000000..b08bf51d89 --- /dev/null +++ b/packages/type-storage/src/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { storage } from './storage'; + +export default storage; diff --git a/packages/type-storage/src/static.ts b/packages/type-storage/src/static.ts new file mode 100644 index 0000000000..a06a32c99d --- /dev/null +++ b/packages/type-storage/src/static.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Metadata } from '@plugnet/types'; +import metadataRpc from '@plugnet/types/Metadata/v0/static'; + +import fromMetadata from './fromMetadata'; + +export default fromMetadata( + new Metadata(metadataRpc).asV0 +); diff --git a/packages/type-storage/src/storage.spec.ts b/packages/type-storage/src/storage.spec.ts new file mode 100644 index 0000000000..e842262079 --- /dev/null +++ b/packages/type-storage/src/storage.spec.ts @@ -0,0 +1,17 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { storage } from './storage'; + +describe('storage', () => { + it('should return well known keys', () => { + expect(typeof storage.substrate).toBe('object'); + expect(storage.substrate.authorityCount).toBeTruthy(); + expect(storage.substrate.authorityPrefix).toBeTruthy(); + expect(storage.substrate.changesTrieConfig).toBeTruthy(); + expect(storage.substrate.code).toBeTruthy(); + expect(storage.substrate.extrinsicIndex).toBeTruthy(); + expect(storage.substrate.heapPages).toBeTruthy(); + }); +}); diff --git a/packages/type-storage/src/storage.ts b/packages/type-storage/src/storage.ts new file mode 100644 index 0000000000..7745bfd70a --- /dev/null +++ b/packages/type-storage/src/storage.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Storage } from './types'; +import * as substrate from './substrate'; + +export const storage: Storage = { + substrate // Prefill storage with well known keys, as not returned by state_getMetadata +}; diff --git a/packages/type-storage/src/substrate.spec.ts b/packages/type-storage/src/substrate.spec.ts new file mode 100644 index 0000000000..c442e3d58e --- /dev/null +++ b/packages/type-storage/src/substrate.spec.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { authorityCount, authorityPrefix, changesTrieConfig, code, extrinsicIndex, heapPages } from './substrate'; + +describe('substrate', () => { + it('authorityCount should return the correct storage key', () => { + expect(authorityCount()).toEqual(Uint8Array.from([36, 58, 97, 117, 116, 104, 58, 108, 101, 110])); // Length-prefixed + }); + + it('authorityPrefix should return the correct storage key', () => { + expect(authorityPrefix()).toEqual(Uint8Array.from([24, 58, 97, 117, 116, 104, 58])); // Length-prefixed + }); + + it('changesTrieConfig should return the correct storage key', () => { + expect(changesTrieConfig()).toEqual(Uint8Array.from([52, 58, 99, 104, 97, 110, 103, 101, 115, 95, 116, 114, 105, 101])); // Length-prefixed + }); + + it('code should return the correct storage key', () => { + expect(code()).toEqual(Uint8Array.from([20, 58, 99, 111, 100, 101])); // Length-prefixed + }); + + it('extrinsicIndex should return the correct storage key', () => { + expect(extrinsicIndex()).toEqual( + Uint8Array.from([64, 58, 101, 120, 116, 114, 105, 110, 115, 105, 99, 95, 105, 110, 100, 101, 120]) // Length-prefixed + ); + }); + + it('heapPages should return the correct storage key', () => { + expect(heapPages()).toEqual(Uint8Array.from([40, 58, 104, 101, 97, 112, 112, 97, 103, 101, 115])); // Length-prefixed + }); +}); diff --git a/packages/type-storage/src/substrate.ts b/packages/type-storage/src/substrate.ts new file mode 100644 index 0000000000..81b0756b7a --- /dev/null +++ b/packages/type-storage/src/substrate.ts @@ -0,0 +1,62 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { StorageFunctionMetadata, StorageFunctionModifier, StorageFunctionType } from '@plugnet/types/Metadata/v0/Modules'; +import { StorageFunction } from '@plugnet/types/primitive/StorageKey'; +import { Text, Vector } from '@plugnet/types'; + +import createFunction from './utils/createFunction'; + +interface SubstrateMetadata { + documentation: string; + type: string; +} + +// Small helper function to factorize code on this page. +const createRuntimeFunction = (method: string, key: string, { documentation, type }: SubstrateMetadata): StorageFunction => + createFunction( + new Text('Substrate'), + new Text(method), + { + documentation: new Vector(Text, [documentation]), + modifier: new StorageFunctionModifier(1), // default + type: new StorageFunctionType(type, 0), + toJSON: (): any => + key + } as StorageFunctionMetadata, + { + isUnhashed: true, + key + } + ); + +export const code = createRuntimeFunction('code', ':code', { + documentation: 'Wasm code of the runtime.', + type: 'Bytes' +}); + +export const heapPages = createRuntimeFunction('heapPages', ':heappages', { + documentation: 'Number of wasm linear memory pages required for execution of the runtime.', + type: 'u64' +}); + +export const authorityCount = createRuntimeFunction('authorityCount', ':auth:len', { + documentation: 'Number of authorities.', + type: 'u32' +}); + +export const authorityPrefix = createRuntimeFunction('authorityPrefix', ':auth:', { + documentation: 'Prefix under which authorities are storied.', + type: 'u32' +}); + +export const extrinsicIndex = createRuntimeFunction('extrinsicIndex', ':extrinsic_index', { + documentation: 'Current extrinsic index (u32) is stored under this key.', + type: 'u32' +}); + +export const changesTrieConfig = createRuntimeFunction('changesTrieConfig', ':changes_trie', { + documentation: 'Changes trie configuration is stored under this key.', + type: 'u32' +}); diff --git a/packages/type-storage/src/types.ts b/packages/type-storage/src/types.ts new file mode 100644 index 0000000000..085457134f --- /dev/null +++ b/packages/type-storage/src/types.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { StorageFunction } from '@plugnet/types/primitive/StorageKey'; + +import * as substrate from './substrate'; + +type Substrate = keyof typeof substrate; + +export interface ModuleStorage { + [key: string]: StorageFunction; +} + +export interface Storage { + [key: string]: ModuleStorage; // Will hold modules returned by state_getMetadata + substrate: { [key in Substrate]: StorageFunction }; +} diff --git a/packages/type-storage/src/utils/createFunction.spec.ts b/packages/type-storage/src/utils/createFunction.spec.ts new file mode 100644 index 0000000000..d74de23fc3 --- /dev/null +++ b/packages/type-storage/src/utils/createFunction.spec.ts @@ -0,0 +1,56 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a, u8aConcat } from '@plugnet/util'; + +import createFunction from './createFunction'; + +describe('createFunction', () => { + it('should create timestamp.now correctly', () => { + expect( + createFunction( + 'Timestamp', + 'Now', + { type: {} } as any + )() + ).toEqual( + Uint8Array.from([64, 14, 73, 68, 207, 217, 141, 111, 76, 195, 116, 209, 111, 90, 78, 63, 156]) // Length-prefixed + ); + }); + + it('allows overrides on key (keeping name)', () => { + expect( + createFunction( + 'Substrate', + 'authorityCount', + { type: {} } as any, + { + isUnhashed: true, + key: ':auth:len' + } + ).method + ).toEqual('authorityCount'); + }); + + it('allows overrides on key (unhashed)', () => { + const key = ':auth:len'; + + expect( + createFunction( + 'Substrate', + 'authorityCount', + { type: {} } as any, + { + isUnhashed: true, + key + } + )() + ).toEqual( + u8aConcat( + Uint8Array.from([key.length << 2]), + stringToU8a(':auth:len') + ) + ); + }); +}); diff --git a/packages/type-storage/src/utils/createFunction.ts b/packages/type-storage/src/utils/createFunction.ts new file mode 100644 index 0000000000..04c682c8be --- /dev/null +++ b/packages/type-storage/src/utils/createFunction.ts @@ -0,0 +1,75 @@ +// Copyright 2017-2019 @polkadot/storage authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { StorageFunctionMetadata, StorageFunctionModifier, StorageFunctionType } from '@plugnet/types/Metadata/v0/Modules'; +import { StorageFunction } from '@plugnet/types/primitive/StorageKey'; +import { Compact, Text, createType, StorageKey, Bytes, U8a } from '@plugnet/types'; +import { assert, isNull, isUndefined, stringLowerFirst, stringToU8a, u8aConcat } from '@plugnet/util'; +import { xxhashAsU8a } from '@plugnet/util-crypto'; +import { PlainType } from '@plugnet/types/Metadata/v2/Storage'; + +export interface CreateItemOptions { + isUnhashed?: boolean; + key?: string; +} + +/** + * From the schema of a function in the module's storage, generate the function + * that will return the correct storage key. + * + * @param schema - The function's definition schema to create the function from. + * The schema is taken from state_getMetadata. + * @param options - Additional options when creating the function. These options + * are not known at runtime (from state_getMetadata), they need to be supplied + * by us manually at compile time. + */ +export default function createFunction (section: Text | string, method: Text | string, meta: StorageFunctionMetadata, options: CreateItemOptions = {}): StorageFunction { + const stringKey = options.key + ? options.key + : `${section.toString()} ${method.toString()}`; + const rawKey = stringToU8a(stringKey); + + // Can only have zero or one argument: + // - storage.balances.freeBalance(address) + // - storage.timestamp.blockPeriod() + const storageFn = (arg?: any): Uint8Array => { + let key = rawKey; + if (meta.type.isMap) { + assert(!isUndefined(arg) && !isNull(arg), `${meta.name} expects one argument`); + + const type = meta.type.asMap.key.toString(); + const param = createType(type, arg).toU8a(false); + + key = u8aConcat(key, param); + } + + // StorageKey is a Bytes, so is length-prefixed + return Compact.addLengthPrefix( + options.isUnhashed + ? key + : xxhashAsU8a(key, 128) + ); + }; + + if (meta.type.isMap && meta.type.asMap.isLinked) { + // TODO: there needs some better way to do this + const keyHash = new U8a(xxhashAsU8a(`head of ${stringKey}`, 128)); + const keyFn: any = () => keyHash; + keyFn.meta = new StorageFunctionMetadata({ + name: meta.name, + modifier: new StorageFunctionModifier('Required'), + type: new StorageFunctionType(new PlainType(meta.type.asMap.key), 0), + default: new Bytes(), + documentation: meta.documentation + }); + storageFn.headKey = new StorageKey(keyFn); + } + + storageFn.meta = meta; + storageFn.method = stringLowerFirst(method.toString()); + storageFn.section = stringLowerFirst(section.toString()); + storageFn.toJSON = (): any => meta.toJSON(); + + return storageFn as StorageFunction; +} diff --git a/packages/types/LICENSE b/packages/types/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/types/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/types/README.md b/packages/types/README.md new file mode 100644 index 0000000000..94a93b42c5 --- /dev/null +++ b/packages/types/README.md @@ -0,0 +1,23 @@ +# @polkadot/types + +Implementation of the types and their (de-)serialisation via [parity-codec](https://github.com/paritytech/parity-codec). + +## Primitive types + +There primitive types are available: [[Bool]], [[Bytes]], [[Data]], [[H160]], [[H256]], [[H512]], [[I8]], [[I16]], [[I32]], [[I64]], [[I128]], [[I256]], [[Method]], [[Moment]], [[Null]], [[StorageData]], [[StorageKey]], [[Text]], [[Type]], [[U8]], [[U16]], [[U32]], [[U64]], [[U128]], [[U256]], [[USize]] + +## Substrate types + +These types implement specific types that are found as part of the Substrate base: [[AccountId]], [[AccountIndex]], [[Address]], [[Amount]], [[AssetOf]], [[AttestedCandidate]], [[AuthorityId]], [[Balance]], [[BalanceLock]], [[BalanceOf]], [[Block]], [[BlockNumber]], [[CodeHash]], [[Digest]], [[Event]], [[EventRecord]], [[Exposure]], [[Extrinsic]], [[ExtrinsicEra]], [[ExtrinsicSignature]], [[Gas]], [[Hash]], [[Header]], [[IndividualExposure]], [[InherentOfflineReport]], [[Justification]], [[Key]], [[KeyValue]], [[LockIdentifier]], [[MisbehaviorReport]], [[NewAccountOutcome]], [[Nonce]], [[Origin]], [[ParaId]], [[Perbill]], [[Permill]], [[PrefabWasmModule]], [[PropIndex]], [[Proposal]], [[ProposalIndex]], [[ReferendumIndex]], [[ReferendumInfo]], [[RewardDestination]], [[Schedule]], [[SeedOf]], [[SessionKey]], [[Signature]], [[SignaturePayload]], [[StakingLedger]], [[StoredPendingChange]], [[UnlockChunk]], [[ValidatorPrefs]], [[VestingSchedule]], [[Vote]], [[VoteIndex]], [[VoteThreshold]], [[WithdrawReasons]] + +## Codec types + +These are the base types of the codec, typically not used directly, but rather inherited from to create specific types: [[Base]], [[Compact]], [[Enum]], [[EnumType]], [[Option]], [[Set]], [[Struct]], [[Tuple]], [[U8a]], [[U8aFixed]], [[UInt]], [[Vector]] + +## RPC types + +These types are not used in the runtime, but rather are used in RPC results: [[ChainProperties]], [[ExtrinsicStatus]], [[Health]], [[Json]], [[NetworkState]], [[Metadata]], [[PeerInfo]], [[PendingExtrinsics]], [[RuntimeVersion]], [[SignedBlock]], [[StorageChangeSet]] + +## Derived types + +Some types are extended from the base to provide additional information: [[HeaderExtended]], [[ReferendumInfoExtended]] diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000000..be593034cc --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,36 @@ +{ + "name": "@plugnet/types", + "version": "0.76.1", + "description": "Implementation of the Parity codec", + "main": "index.js", + "keywords": [ + "Polkadot", + "JsonRPC" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/api.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/api/issues" + }, + "homepage": "https://github.com/polkadot-js/api/tree/master/packages/types#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/keyring": "^0.76.1", + "@plugnet/util": "^0.76.1" + } +} diff --git a/packages/types/src/ContractAbi.ts b/packages/types/src/ContractAbi.ts new file mode 100644 index 0000000000..cce5c187f8 --- /dev/null +++ b/packages/types/src/ContractAbi.ts @@ -0,0 +1,148 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { CodecArg, Constructor } from './types'; + +import { assert, isNumber, isNull, isString, isUndefined, stringCamelCase } from '@plugnet/util'; + +import Compact from './codec/Compact'; +import { createClass } from './codec/createType'; + +export type ContractABIArgs = Array<{ + name: string, + type: string +}>; + +export type ContractABIMethodBase = { + args: ContractABIArgs +}; + +export type ContractABIMethod = ContractABIMethodBase & { + mutates?: boolean, + name: string, + selector: number, + return_type: string | null +}; + +export type ContractABI = { + deploy: ContractABIMethodBase, + messages: Array, + name: string +}; + +export interface ContractABIFn { + (...args: Array): Uint8Array; + args: ContractABIArgs; + isConstant: boolean; + type: string | null; +} + +export interface Contract { + abi: ContractABI; + deploy: ContractABIFn; + messages: { + [index: string]: ContractABIFn + }; +} + +export function validateArgs (name: string, args: ContractABIArgs): void { + assert(Array.isArray(args), `Expected 'args' to exist on ${name}`); + + args.forEach((arg) => { + const unknownKeys = Object.keys(arg).filter((key) => !['name', 'type'].includes(key)); + + assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for ${name}`); + assert(isString(arg.name) && isString(arg.type), `${name} args should have valid name & type values`); + }); +} + +export function validateDeploy ({ deploy }: ContractABI): void { + const unknownKeys = Object.keys(deploy).filter((key) => !['args'].includes(key)); + + assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI deploy`); + + validateArgs('deploy', deploy.args); +} + +export function validateMethods ({ messages }: ContractABI): void { + messages.forEach((method) => { + const unknownKeys = Object.keys(method).filter((key) => !['args', 'mutates', 'name', 'selector', 'return_type'].includes(key)); + + assert(unknownKeys.length === 0, `Unknown keys ${unknownKeys.join(', ')} found in ABI args for messages.${name}`); + assert(isString(method.name) && isNumber(method.selector) && (isNull(method.return_type) || isString(method.return_type)), `Expected name, selector & return_type specified for messages.${method.name}`); + + validateArgs(`messages.${method.name}`, method.args); + }); +} + +export function validateAbi (abi: ContractABI): void { + const unknownKeys = Object.keys(abi).filter((key) => !['deploy', 'messages', 'name'].includes(key)); + + assert(unknownKeys.length === 0, `Unknown fields ${unknownKeys.join(', ')} found in ABI`); + assert(abi.deploy && abi.messages && isString(abi.name), 'ABI should have deploy, messages & name sections'); + + validateDeploy(abi); + validateMethods(abi); +} + +export default class ContractAbi implements Contract { + abi: ContractABI; + deploy: ContractABIFn; + messages: { [index: string]: ContractABIFn } = {}; + + constructor (abi: ContractABI) { + validateAbi(abi); + + this.abi = abi; + this.deploy = this._createEncoded('deploy', abi.deploy); + + abi.messages.forEach((method) => { + const name = stringCamelCase(method.name); + + this.messages[name] = this._createEncoded(`messages.${name}`, method); + }); + } + + private _createClazz (args: ContractABIArgs, isDeploy: boolean): Constructor { + return createClass( + JSON.stringify( + args.reduce((base: { [index: string]: string }, { name, type }) => { + base[name] = type; + + return base; + }, isDeploy ? {} : { __selector: 'u32' }) + ) + ); + } + + private _createEncoded (name: string, method: Partial & ContractABIMethodBase): ContractABIFn { + const args = method.args.map(({ name, type }) => ({ + name: stringCamelCase(name), + type + })); + const Clazz = this._createClazz(args, isUndefined(method.selector)); + const base: { [index: string]: any } = { __selector: method.selector }; + const encoder = (...params: Array): Uint8Array => { + assert(params.length === args.length, `Expected ${args.length} arguments to contract ${name}, found ${params.length}`); + + return Compact.addLengthPrefix( + new Clazz( + args.reduce((mapped, { name }, index) => { + mapped[name] = params[index]; + + return mapped; + }, { ...base }) + ).toU8a() + ); + }; + + const fn = (encoder as ContractABIFn); + + fn.args = args; + fn.isConstant = !(method.mutates || false); + fn.type = method.return_type || null; + + return fn; + } +} diff --git a/packages/types/src/Metadata/MagicNumber.spec.ts b/packages/types/src/Metadata/MagicNumber.spec.ts new file mode 100644 index 0000000000..15edfbfed8 --- /dev/null +++ b/packages/types/src/Metadata/MagicNumber.spec.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import MagicNumber, { MAGIC_NUMBER } from './MagicNumber'; + +describe('MagicNumber', () => { + it('succeeds when the magic number matches', () => { + expect(() => new MagicNumber(MAGIC_NUMBER)).not.toThrow(); + }); + + it('fails when the magic number missmatches', () => { + expect(() => new MagicNumber(0x12345)).toThrow(/MagicNumber/); + }); +}); diff --git a/packages/types/src/Metadata/MagicNumber.ts b/packages/types/src/Metadata/MagicNumber.ts new file mode 100644 index 0000000000..bbeebcf2e8 --- /dev/null +++ b/packages/types/src/Metadata/MagicNumber.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert } from '@plugnet/util'; + +import U32 from '../primitive/U32'; + +export const MAGIC_NUMBER = new U32(0x6174656d); // `meta`, reversed for LE encoding +export const MAGIC_ERROR = -61746; + +export default class MagicNumber extends U32 { + constructor (value?: any) { + super(value); + + assert(this.eq(MAGIC_NUMBER), `MagicNumber: expected ${MAGIC_NUMBER.toHex()}, found ${this.toHex()}`, MAGIC_ERROR); + } +} diff --git a/packages/types/src/Metadata/Metadata.ts b/packages/types/src/Metadata/Metadata.ts new file mode 100644 index 0000000000..9e86efc139 --- /dev/null +++ b/packages/types/src/Metadata/Metadata.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isHex, hexToU8a, u8aConcat } from '@plugnet/util'; + +import { MAGIC_ERROR, MAGIC_NUMBER } from './MagicNumber'; +import MetadataVersioned from './MetadataVersioned'; + +/** + * @name Metadata + * @description + * The versioned runtime metadata as a decoded structure + */ +export default class Metadata extends MetadataVersioned { + constructor (value?: Uint8Array | string) { + super(Metadata.decodeMetadata(value)); + } + + // first we try and parse using the versioned structure, if this does fail, + // we adjust with the magic number and a manual version and re-try. As soon as + // we remove support for V0, we will just do a new here + private static decodeMetadata (_value: Uint8Array | string = new Uint8Array()): MetadataVersioned { + const value = isHex(_value) + ? hexToU8a(_value) + : _value; + + try { + return new MetadataVersioned(value); + } catch (error) { + if (error.code !== MAGIC_ERROR) { + throw error; + } + } + + return new MetadataVersioned( + u8aConcat( + MAGIC_NUMBER.toU8a(), // manually add the magic number + Uint8Array.from([0]), // add the version for the original + value // the actual data as retrieved + ) + ); + } +} diff --git a/packages/types/src/Metadata/MetadataVersioned.ts b/packages/types/src/Metadata/MetadataVersioned.ts new file mode 100644 index 0000000000..8865d92d9e --- /dev/null +++ b/packages/types/src/Metadata/MetadataVersioned.ts @@ -0,0 +1,166 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { MetadataInterface } from './types'; + +import { assert, isUndefined } from '@plugnet/util'; + +import EnumType from '../codec/EnumType'; +import Struct from '../codec/Struct'; +import Null from '../primitive/Null'; +import MagicNumber from './MagicNumber'; +import MetadataV0 from './v0'; +import MetadataV1 from './v1'; +import MetadataV2 from './v2'; +import MetadataV3 from './v3'; +import v1ToV0 from './v1/toV0'; +import v2ToV1 from './v2/toV1'; +import v3ToV2 from './v3/toV2'; + +class MetadataEnum extends EnumType { + constructor (value?: any) { + super({ + MetadataV0, // once rolled-out, can replace this with Null + MetadataV1, // once rolled-out, can replace this with Null + MetadataV2, // once rolled-out, can replace this with Null + MetadataV3 + }, value); + } + + /** + * @description Returns the wrapped values as a V0 object + */ + get asV0 (): MetadataV0 { + return this.value as MetadataV0; + } + + /** + * @description Returns the wrapped values as a V1 object + */ + get asV1 (): MetadataV1 { + return this.value as MetadataV1; + } + + /** + * @description Returns the wrapped values as a V2 object + */ + get asV2 (): MetadataV2 { + return this.value as MetadataV2; + } + + /** + * @description Returns the wrapped values as a V3 object + */ + get asV3 (): MetadataV3 { + return this.value as MetadataV3; + } + + /** + * @description The version this metadata represents + */ + get version (): number { + return this.index; + } +} + +/** + * @name MetadataVersioned + * @description + * The versioned runtime metadata as a decoded structure + */ +export default class MetadataVersioned extends Struct implements MetadataInterface { + private _convertedV0?: MetadataV0; + private _convertedV1?: MetadataV1; + private _convertedV2?: MetadataV2; + + constructor (value?: any) { + super({ + magicNumber: MagicNumber, + metadata: MetadataEnum + }, value); + } + + /** + * @description + */ + get magicNumber (): MagicNumber { + return this.get('magicNumber') as MagicNumber; + } + + /** + * @description the metadata wrapped + */ + private get metadata (): MetadataEnum { + return this.get('metadata') as MetadataEnum; + } + + /** + * @description the metadata version this structure represents + */ + get version (): number { + return this.metadata.index; + } + + /** + * @description Returns the wrapped metadata as a V0 object + */ + get asV0 (): MetadataV0 { + if (this.metadata.version === 0) { + return this.metadata.asV0; + } + + if (isUndefined(this._convertedV0)) { + this._convertedV0 = v1ToV0(this.asV1); + } + + return this._convertedV0; + } + + /** + * @description Returns the wrapped values as a V1 object + */ + get asV1 (): MetadataV1 { + if (this.metadata.version === 1) { + return this.metadata.asV1; + } + + assert([2, 3].includes(this.metadata.version), `Cannot convert metadata from v${this.metadata.version} to v1`); + + if (isUndefined(this._convertedV1)) { + this._convertedV1 = v2ToV1(this.metadata.asV2); + } + + return this._convertedV1; + } + + /** + * @description Returns the wrapped values as a V2 object + */ + get asV2 (): MetadataV2 { + if (this.metadata.version === 2) { + return this.metadata.asV2; + } + + assert(this.metadata.version === 3, `Cannot convert metadata from v${this.metadata.version} to v1`); + + if (isUndefined(this._convertedV2)) { + this._convertedV2 = v3ToV2(this.metadata.asV3); + } + + return this._convertedV2; + } + + /** + * @description Returns the wrapped values as a V3 object + */ + get asV3 (): MetadataV3 { + assert(this.metadata.version === 3, `Cannot convert metadata from v${this.metadata.version} to v3`); + + return this.metadata.asV3; + } + + getUniqTypes (throwError: boolean): Array { + return (this.metadata.value as any as MetadataInterface).getUniqTypes(throwError); + } +} diff --git a/packages/types/src/Metadata/README.md b/packages/types/src/Metadata/README.md new file mode 100644 index 0000000000..37ee0663f1 --- /dev/null +++ b/packages/types/src/Metadata/README.md @@ -0,0 +1,12 @@ +# metadata update + +1. New branch :) +2. `curl -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "state_getMetadata", "params":[]}' http://localhost:9933` +3. Copy the result (hex-only) into `tstaic.ts`, as of now linked to `v1/static.ts` +4. Rust Metadata tests, `yarn run test packages/types/src/Metadata` +5. Add new class types as required (find missing in Rust code, add) +6. Add all classes to `index.classes.ts` +7. Repeat 4-6 until passing +8. Update parsed JSON to actual output version, `v1/latest.*.json` +9. All tests +10. Make PR diff --git a/packages/types/src/Metadata/index.ts b/packages/types/src/Metadata/index.ts new file mode 100644 index 0000000000..5e105166a4 --- /dev/null +++ b/packages/types/src/Metadata/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as Metadata } from './Metadata'; + +export default Metadata; diff --git a/packages/types/src/Metadata/static.ts b/packages/types/src/Metadata/static.ts new file mode 100644 index 0000000000..7cd53b6261 --- /dev/null +++ b/packages/types/src/Metadata/static.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import metadata from './v3/static'; + +export default metadata; diff --git a/packages/types/src/Metadata/types.ts b/packages/types/src/Metadata/types.ts new file mode 100644 index 0000000000..57b2fa65df --- /dev/null +++ b/packages/types/src/Metadata/types.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export interface MetadataInterface { + getUniqTypes (throwError: boolean): Array; +} diff --git a/packages/types/src/Metadata/util/flattenUniq.ts b/packages/types/src/Metadata/util/flattenUniq.ts new file mode 100644 index 0000000000..0c3fbdb244 --- /dev/null +++ b/packages/types/src/Metadata/util/flattenUniq.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// Quick and dirty flatten (.flat() not available) +export default function flattenUniq (list: Array): Array { + const flat = list.reduce((result, entry) => { + return result.concat( + Array.isArray(entry) + ? flattenUniq(entry) + : entry + ); + }, []); + + return [...new Set(flat)] + .filter((value: any) => value) + .sort(); +} diff --git a/packages/types/src/Metadata/util/index.ts b/packages/types/src/Metadata/util/index.ts new file mode 100644 index 0000000000..ebc1c56f00 --- /dev/null +++ b/packages/types/src/Metadata/util/index.ts @@ -0,0 +1,6 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { default as flattenUniq } from './flattenUniq'; +export { default as validateTypes } from './validateTypes'; diff --git a/packages/types/src/Metadata/util/validateTypes.ts b/packages/types/src/Metadata/util/validateTypes.ts new file mode 100644 index 0000000000..bf245b4c21 --- /dev/null +++ b/packages/types/src/Metadata/util/validateTypes.ts @@ -0,0 +1,50 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isUndefined } from '@plugnet/util'; + +import { getTypeDef, TypeDef, TypeDefInfo } from '../../codec/createType'; +import flattenUniq from './flattenUniq'; +import { getTypeRegistry } from '../../codec'; + +export default function validateTypes (types: Array, throwError: boolean): void { + const extractTypes = (types: Array): Array => { + return types.map((type) => { + const decoded = getTypeDef(type); + + switch (decoded.info) { + case TypeDefInfo.Plain: + return decoded.type; + + case TypeDefInfo.Compact: + case TypeDefInfo.Option: + case TypeDefInfo.Vector: + return extractTypes([(decoded.sub as TypeDef).type]); + + case TypeDefInfo.Tuple: + return extractTypes( + (decoded.sub as Array).map((sub) => sub.type) + ); + + default: + throw new Error('Unreachable'); + } + }); + }; + + const typeRegistry = getTypeRegistry(); + const missing = flattenUniq(extractTypes(types)).filter((type) => + isUndefined(typeRegistry.get(type)) + ); + + if (missing.length !== 0) { + const message = `Unknown types found, no types for ${missing}`; + + if (throwError) { + throw new Error(message); + } else { + console.error(message); + } + } +} diff --git a/packages/types/src/Metadata/v0/Calls.ts b/packages/types/src/Metadata/v0/Calls.ts new file mode 100644 index 0000000000..8a447fd295 --- /dev/null +++ b/packages/types/src/Metadata/v0/Calls.ts @@ -0,0 +1,62 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import U16 from '../../primitive/U16'; + +export class OuterDispatchCall extends Struct { + constructor (value?: any) { + super({ + name: Text, + prefix: Text, + index: U16 + }, value); + } + + /** + * @description The [[U16]] index for the call + */ + get index (): U16 { + return this.get('index') as U16; + } + + /** + * @description The name for the call + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The call prefix (or section) + */ + get prefix (): Text { + return this.get('prefix') as Text; + } +} + +export class OuterDispatchMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + calls: Vector.with(OuterDispatchCall) + }, value); + } + + /** + * @description The [[OuterDispathCall]] wrapped + */ + get calls (): Vector { + return this.get('calls') as Vector; + } + + /** + * @description The name for the dispatch + */ + get name (): Text { + return this.get('name') as Text; + } +} diff --git a/packages/types/src/Metadata/v0/Events.ts b/packages/types/src/Metadata/v0/Events.ts new file mode 100644 index 0000000000..dc4a517438 --- /dev/null +++ b/packages/types/src/Metadata/v0/Events.ts @@ -0,0 +1,86 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../../codec/Struct'; +import Tuple from '../../codec/Tuple'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; + +export class EventMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + arguments: Vector.with(Type), + documentation: Vector.with(Text) + }, value); + } + + /** + * @description The arguments of [[Type]] + */ + get arguments (): Vector { + return this.get('arguments') as Vector; + } + + /** + * @description The [[Text]] documentation + */ + get documentation (): Vector { + return this.get('documentation') as Vector; + } + + /** + * @description The name for the event + */ + get name (): Text { + return this.get('name') as Text; + } +} + +export class OuterEventMetadataEvent extends Tuple { + constructor (value?: any) { + super({ + Text, + 'Vec': Vector.with(EventMetadata) + }, value); + } + + /** + * @description The [[EventMetadata]] + */ + get events (): Vector { + return this[1] as Vector; + } + + /** + * @description The name of the section + */ + get name (): Text { + return this[0] as Text; + } +} + +export class OuterEventMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + events: Vector.with(OuterEventMetadataEvent) + }, value); + } + + /** + * @description The [[OuterEventMetadataEvent]] + */ + get events (): Vector { + return this.get('events') as Vector; + } + + /** + * @description The name of the event + */ + get name (): Text { + return this.get('name') as Text; + } +} diff --git a/packages/types/src/Metadata/v0/Metadata.spec.ts b/packages/types/src/Metadata/v0/Metadata.spec.ts new file mode 100644 index 0000000000..2a4c693010 --- /dev/null +++ b/packages/types/src/Metadata/v0/Metadata.spec.ts @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata'; + +import createType from '../../codec/createType'; +import Method from '../../primitive/Method'; + +import Metadata from './Metadata'; +import latestParsed from './latest.substrate.json'; +import rpcData from './static'; + +describe('Metadata', () => { + it('decodes latest properly', () => { + const metadata = new Metadata(rpcData); + const str = JSON.stringify(metadata.toJSON()); + + expect(metadata.events.length).not.toBe(0); + expect(str).toEqual(JSON.stringify(latestParsed)); + }); + + describe('storage with default values', () => { + const metadata = new Metadata(rpcData); + + Method.injectMethods(extrinsicsFromMeta(metadata)); + + metadata.modules.forEach((mod) => { + if (mod.storage.isNone) { + return; + } + + mod.storage.unwrap().functions.forEach((fn) => { + it(`creates default types for ${mod.prefix}.${fn.name}, type ${fn.type}`, () => { + expect( + () => createType(fn.type.toString(), fn.default) + ).not.toThrow(); + }); + }); + }); + }); +}); diff --git a/packages/types/src/Metadata/v0/Metadata.ts b/packages/types/src/Metadata/v0/Metadata.ts new file mode 100644 index 0000000000..a61fdd0488 --- /dev/null +++ b/packages/types/src/Metadata/v0/Metadata.ts @@ -0,0 +1,116 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { MetadataInterface } from '../types'; + +import { hexToU8a, isHex, isU8a } from '@plugnet/util'; + +import Compact from '../../codec/Compact'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import { flattenUniq, validateTypes } from '../util'; +import { OuterDispatchMetadata, OuterDispatchCall } from './Calls'; +import { OuterEventMetadata, OuterEventMetadataEvent } from './Events'; +import { RuntimeModuleMetadata } from './Modules'; + +// Decodes the runtime metadata as passed through from the `state_getMetadata` call. This +// file is probably best understood from the bottom-up, i.e. start reading right at the +// end and work up. (Just so we don't use before definition) + +/** + * @name MetadataV0 + * @description + * The runtime metadata as a decoded structure + */ +export default class MetadataV0 extends Struct implements MetadataInterface { + constructor (value?: any) { + super({ + outerEvent: OuterEventMetadata, + modules: Vector.with(RuntimeModuleMetadata), + outerDispatch: OuterDispatchMetadata + }, MetadataV0.decodeMetadata(value)); + } + + static decodeMetadata (value: string | Uint8Array | object): object | Uint8Array { + if (isHex(value)) { + // We receive this as an hex in the JSON output from the Node. + // Convert to u8a and use the U8a version to do the actual parsing. + return MetadataV0.decodeMetadata(hexToU8a(value)); + } else if (isU8a(value)) { + // HACK 13 Oct 2018 - For current running BBQ nodes, Metadata is not properly + // encoded, it does not have a length prefix. For latest substrate master, it + // is properly encoded. Here we pull the prefix, check it agianst the length - + // if matches, then we have the length, otherwise we assume it is an older node + // and use the whole buffer + const [offset, length] = Compact.decodeU8a(value); + + return value.length === (offset + length.toNumber()) + ? value.subarray(offset) + : value; + } + + // Decode as normal struct + return value; + } + + /** + * @description Wrapped [[OuterDispatchCall]] + */ + get calls (): Vector { + return (this.get('outerDispatch') as OuterDispatchMetadata).calls; + } + + /** + * @description Wrapped [[OuterEventMetadataEvent]] + */ + get events (): Vector { + return (this.get('outerEvent') as OuterEventMetadata).events; + } + + /** + * @description Wrapped [[RuntimeModuleMetadata]] + */ + get modules (): Vector { + return this.get('modules') as Vector; + } + + private get argNames () { + return this.modules.map((module) => + module.module.call.functions.map((fn) => + fn.arguments.map((argument) => argument.type.toString()) + ) + ); + } + + private get eventNames () { + return this.events.map((module) => + module.events.map((event) => + event.arguments.map((argument) => argument.toString()) + ) + ); + } + + private get storageNames () { + return this.modules.map((module) => + module.storage.isSome + ? module.storage.unwrap().functions.map((fn) => + fn.type.isMap + ? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()] + : [fn.type.asType.toString()] + ) + : [] + ); + } + + /** + * @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated + */ + getUniqTypes (throwError: boolean): Array { + const types = flattenUniq([this.argNames, this.eventNames, this.storageNames]); + + validateTypes(types, throwError); + + return types; + } +} diff --git a/packages/types/src/Metadata/v0/Modules.ts b/packages/types/src/Metadata/v0/Modules.ts new file mode 100644 index 0000000000..eb72da7a67 --- /dev/null +++ b/packages/types/src/Metadata/v0/Modules.ts @@ -0,0 +1,327 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../../types'; + +import Enum from '../../codec/Enum'; +import EnumType from '../../codec/EnumType'; +import Option from '../../codec/Option'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Bytes from '../../primitive/Bytes'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; +import U16 from '../../primitive/U16'; + +export class FunctionArgumentMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + type: Type + }, value); + } + + /** + * @description The argument name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The [[Type]] + */ + get type (): Type { + return this.get('type') as Type; + } +} + +export class FunctionMetadata extends Struct { + constructor (value?: any) { + super({ + id: U16, + name: Text, + arguments: Vector.with(FunctionArgumentMetadata), + documentation: Vector.with(Text) + }, value); + } + + /** + * @description The [[FunctionArgumentMetadata]] for arguments + */ + get arguments (): Vector { + return this.get('arguments') as Vector; + } + + /** + * @description The [[Text]] documentation + */ + get documentation (): Vector { + return this.get('documentation') as Vector; + } + + /** + * @description The `[sectionIndex, methodIndex]` call id + */ + get id (): U16 { + return this.get('id') as U16; + } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } +} + +export class CallMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + functions: Vector.with(FunctionMetadata) + }, value); + } + + /** + * @description The functions available as [[FunctionMetadata]] + */ + get functions (): Vector { + return this.get('functions') as Vector; + } + + /** + * @description The section name + */ + get name (): Text { + return this.get('name') as Text; + } +} + +export class ModuleMetadata extends Struct { + constructor (value?: any) { + super({ + name: Text, + call: CallMetadata + }, value); + } + + /** + * @description The calls as [[CallMetadata]] + */ + get call (): CallMetadata { + return this.get('call') as CallMetadata; + } + + /** + * @description The name + */ + get name (): Text { + return this.get('name') as Text; + } +} + +export class StorageFunctionModifier extends Enum { + constructor (value?: any) { + super(['Optional', 'Default', 'Required'], value); + } + + /** + * @description `true` if the storage entry is optional + */ + get isOptional (): boolean { + return this.toNumber() === 0; + } +} + +export class MapType extends Struct { + private _isLinked = false; + + constructor (value?: any) { + super({ + key: Type, + value: Type + }, value); + + if (value && value.isLinked) { + this._isLinked = true; + } + } + + /** + * @description The mapped key as [[Type]] + */ + get key (): Type { + return this.get('key') as Type; + } + + /** + * @description The mapped value as [[Type]] + */ + get value (): Type { + return this.get('value') as Type; + } + + /** + * @description Is this an enumerable linked map + */ + get isLinked (): boolean { + return this._isLinked; + } +} + +class PlainType extends Type { +} + +export class StorageFunctionType extends EnumType { + constructor (value?: any, index?: number) { + super({ + PlainType, + MapType + }, value, index); + } + + /** + * @description `true` if the storage entry is a map + */ + get isMap (): boolean { + return this.toNumber() === 1; + } + + /** + * @description The value as a mapped value + */ + get asMap (): MapType { + return this.value as MapType; + } + + /** + * @description The value as a [[Type]] value + */ + get asType (): PlainType { + return this.value as PlainType; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + if (this.isMap) { + if (this.asMap.isLinked) { + return `(${this.asMap.value.toString()}, Linkage<${this.asMap.key.toString()}>)`; + } + return this.asMap.value.toString(); + } + return this.asType.toString(); + } +} + +type StorageFunctionMetadataValue = { + name: string | Text, + modifier: StorageFunctionModifier | AnyNumber, + type: StorageFunctionType, + default: Bytes, + documentation: Vector | Array +}; + +export class StorageFunctionMetadata extends Struct { + constructor (value?: StorageFunctionMetadataValue | Uint8Array) { + super({ + name: Text, + modifier: StorageFunctionModifier, + type: StorageFunctionType, + default: Bytes, + documentation: Vector.with(Text) + }, value); + } + + /** + * @description The default value of the storage function + */ + get default (): Bytes { + return this.get('default') as Bytes; + } + + /** + * @description The [[Text]] documentation + */ + get documentation (): Vector { + return this.get('documentation') as Vector; + } + + /** + * @description The key name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The modifier + */ + get modifier (): StorageFunctionModifier { + return this.get('modifier') as StorageFunctionModifier; + } + + /** + * @description The [[StorageFunctionType]] + */ + get type (): StorageFunctionType { + return this.get('type') as StorageFunctionType; + } +} + +export class StorageMetadata extends Struct { + constructor (value?: any) { + super({ + prefix: Text, + functions: Vector.with(StorageFunctionMetadata) + }, value); + } + + /** + * @description The [[StorageFunctionMetadata]] for the section + */ + get functions (): Vector { + return this.get('functions') as Vector; + } + + /** + * @description The section prefix + */ + get prefix (): Text { + return this.get('prefix') as Text; + } +} + +export class RuntimeModuleMetadata extends Struct { + constructor (value?: any) { + super({ + prefix: Text, + module: ModuleMetadata, + storage: Option.with(StorageMetadata) + }, value); + } + + /** + * @description The [[ModuleMetadata]] + */ + get module (): ModuleMetadata { + return this.get('module') as ModuleMetadata; + } + + /** + * @description The prefix + */ + get prefix (): Text { + return this.get('prefix') as Text; + } + + /** + * @description The optional [[StorageMetadata]] + */ + get storage (): Option { + return this.get('storage') as Option; + } +} diff --git a/packages/types/src/Metadata/v0/index.ts b/packages/types/src/Metadata/v0/index.ts new file mode 100644 index 0000000000..379c902089 --- /dev/null +++ b/packages/types/src/Metadata/v0/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as MetadataV0 } from './Metadata'; + +export default MetadataV0; diff --git a/packages/types/src/Metadata/v0/latest.polkadot.json b/packages/types/src/Metadata/v0/latest.polkadot.json new file mode 100644 index 0000000000..6c98d99a8e --- /dev/null +++ b/packages/types/src/Metadata/v0/latest.polkadot.json @@ -0,0 +1,2429 @@ +{ + "outerEvent": { + "name": "Event", + "events": [ + [ + "system", + [ + { + "name": "ExtrinsicSuccess", + "arguments": [], + "documentation": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "arguments": [], + "documentation": [ + " An extrinsic failed." + ] + } + ] + ], + [ + "balances", + [ + { + "name": "NewAccount", + "arguments": [ + "AccountId", + "AccountIndex", + "NewAccountOutcome" + ], + "documentation": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "arguments": [ + "AccountId" + ], + "documentation": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "arguments": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "documentation": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + ], + [ + "session", + [ + { + "name": "NewSession", + "arguments": [ + "BlockNumber" + ], + "documentation": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + ], + [ + "staking", + [ + { + "name": "Reward", + "arguments": [ + "Balance" + ], + "documentation": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "arguments": [ + "AccountId", + "u32" + ], + "documentation": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "arguments": [ + "AccountId", + "Balance" + ], + "documentation": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + ], + [ + "democracy", + [ + { + "name": "Tabled", + "arguments": [ + "PropIndex", + "Balance", + "Vec" + ], + "documentation": [] + }, + { + "name": "Started", + "arguments": [ + "ReferendumIndex", + "VoteThreshold" + ], + "documentation": [] + }, + { + "name": "Passed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "NotPassed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Cancelled", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Executed", + "arguments": [ + "ReferendumIndex", + "bool" + ], + "documentation": [] + } + ] + ], + [ + "grandpa", + [ + { + "name": "NewAuthorities", + "arguments": [ + "Vec<(SessionKey,u64)>" + ], + "documentation": [ + " New authority set has been applied." + ] + } + ] + ], + [ + "council", + [ + { + "name": "VoterReaped", + "arguments": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "arguments": [ + "AccountId" + ], + "documentation": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "arguments": [ + "u32" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalised", + "arguments": [ + "Vec", + "Vec" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + ], + [ + "council_voting", + [ + { + "name": "TallyCancelation", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + ], + [ + "council_motions", + [ + { + "name": "Proposed", + "arguments": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "documentation": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "arguments": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "documentation": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "arguments": [ + "Hash", + "bool" + ], + "documentation": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + ], + [ + "treasury", + [ + { + "name": "Proposed", + "arguments": [ + "ProposalIndex" + ], + "documentation": [ + " New proposal." + ] + }, + { + "name": "Spending", + "arguments": [ + "Balance" + ], + "documentation": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "arguments": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "documentation": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "arguments": [ + "Balance" + ], + "documentation": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "arguments": [ + "Balance" + ], + "documentation": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + ] + ] + }, + "modules": [ + { + "prefix": "system", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": { + "prefix": "System", + "functions": [ + { + "name": "AccountNonce", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Index" + }, + "documentation": [] + }, + { + "name": "ExtrinsicCount", + "modifier": 0, + "type": "u32", + "documentation": [] + }, + { + "name": "BlockHash", + "modifier": 1, + "type": { + "key": "BlockNumber", + "value": "Hash" + }, + "documentation": [] + }, + { + "name": "ExtrinsicData", + "modifier": 1, + "type": { + "key": "u32", + "value": "Bytes" + }, + "documentation": [] + }, + { + "name": "RandomSeed", + "modifier": 1, + "type": "Hash", + "documentation": [] + }, + { + "name": "Number", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": 1, + "type": "Hash", + "documentation": [] + }, + { + "name": "ExtrinsicsRoot", + "modifier": 1, + "type": "Hash", + "documentation": [] + }, + { + "name": "Digest", + "modifier": 1, + "type": "Digest", + "documentation": [] + }, + { + "name": "Events", + "modifier": 1, + "type": "Vec", + "documentation": [] + } + ] + } + }, + { + "prefix": "aura", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": null + }, + { + "prefix": "timestamp", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set", + "arguments": [ + { + "name": "now", + "type": "Compact" + } + ], + "documentation": [ + " Set the current time.", + "", + " Extrinsic with this call should be placed at the specific position in the each block", + " (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block.", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `block_period`." + ] + } + ] + } + }, + "storage": { + "prefix": "Timestamp", + "functions": [ + { + "name": "Now", + "modifier": 1, + "type": "Moment", + "documentation": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": 1, + "type": "Moment", + "documentation": [ + " The minimum (and advised) period between blocks." + ] + }, + { + "name": "DidUpdate", + "modifier": 1, + "type": "bool", + "documentation": [ + " Did the timestamp get updated in this block?" + ] + } + ] + } + }, + { + "prefix": "consensus", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + }, + { + "id": 1, + "name": "note_offline", + "arguments": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "documentation": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "id": 2, + "name": "remark", + "arguments": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "documentation": [ + " Make some on-chain remark." + ] + }, + { + "id": 3, + "name": "set_heap_pages", + "arguments": [ + { + "name": "pages", + "type": "u64" + } + ], + "documentation": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "id": 4, + "name": "set_code", + "arguments": [ + { + "name": "new", + "type": "Bytes" + } + ], + "documentation": [ + " Set the new code." + ] + }, + { + "id": 5, + "name": "set_storage", + "arguments": [ + { + "name": "items", + "type": "Vec" + } + ], + "documentation": [ + " Set some items of storage." + ] + } + ] + } + }, + "storage": { + "prefix": "Consensus", + "functions": [ + { + "name": "OriginalAuthorities", + "modifier": 0, + "type": "Vec", + "documentation": [] + } + ] + } + }, + { + "prefix": "balances", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "transfer", + "arguments": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Transfer some liquid free balance to another staker." + ] + }, + { + "id": 1, + "name": "set_balance", + "arguments": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "documentation": [ + " Set the balances of a given account." + ] + } + ] + } + }, + "storage": { + "prefix": "Balances", + "functions": [ + { + "name": "TotalIssuance", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The total amount of stake on the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The minimum amount allowed to keep an account open." + ] + }, + { + "name": "ReclaimRebate", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The amount credited to a destination's account whose index was reclaimed." + ] + }, + { + "name": "TransferFee", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The fee required to create an account. At least as big as ReclaimRebate." + ] + }, + { + "name": "NextEnumSet", + "modifier": 1, + "type": "AccountIndex", + "documentation": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": 1, + "type": { + "key": "AccountIndex", + "value": "Vec" + }, + "documentation": [ + " The enumeration sets." + ] + }, + { + "name": "FreeBalance", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Balance" + }, + "documentation": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It is", + " alone used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to cleanup data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Balance" + }, + "documentation": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable. (This is different", + " and wholly unrelated to the `Bondage` system used in the staking module.)", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + } + ] + } + }, + { + "prefix": "session", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_key", + "arguments": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "documentation": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "id": 1, + "name": "set_length", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "id": 2, + "name": "force_new_session", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Forces a new session." + ] + } + ] + } + }, + "storage": { + "prefix": "Session", + "functions": [ + { + "name": "Validators", + "modifier": 1, + "type": "Vec", + "documentation": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": 1, + "type": "Moment", + "documentation": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": 0, + "type": "bool", + "documentation": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": 0, + "type": "BlockNumber", + "documentation": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": 0, + "type": { + "key": "AccountId", + "value": "SessionKey" + }, + "documentation": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": 0, + "type": "BlockNumber", + "documentation": [ + " The next session length." + ] + } + ] + } + }, + { + "prefix": "staking", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "stake", + "arguments": [], + "documentation": [ + " Declare the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 1, + "name": "unstake", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + } + ], + "documentation": [ + " Retract the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 2, + "name": "nominate", + "arguments": [ + { + "name": "target", + "type": "Address" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "unnominate", + "arguments": [ + { + "name": "target_index", + "type": "Compact" + } + ], + "documentation": [ + " Will panic if called when source isn't currently nominating target.", + " Updates Nominating, NominatorsFor and NominationBalance." + ] + }, + { + "id": 4, + "name": "register_preferences", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + }, + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "documentation": [ + " Set the given account's preference for slashing behaviour should they be a validator.", + "", + " An error (no-op) if `Self::intentions()[intentions_index] != origin`." + ] + }, + { + "id": 5, + "name": "set_sessions_per_era", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the number of sessions in an era." + ] + }, + { + "id": 6, + "name": "set_bonding_duration", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The length of the bonding duration in eras." + ] + }, + { + "id": 7, + "name": "set_validator_count", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The ideal number of validators." + ] + }, + { + "id": 8, + "name": "force_new_era", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "id": 9, + "name": "set_offline_slash_grace", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the offline slash grace period." + ] + } + ] + } + }, + "storage": { + "prefix": "Staking", + "functions": [ + { + "name": "ValidatorCount", + "modifier": 1, + "type": "u32", + "documentation": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": 1, + "type": "u32", + "documentation": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": 1, + "type": "Perbill", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": 1, + "type": "Perbill", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": 1, + "type": "u32", + "documentation": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "CurrentEra", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The current era index." + ] + }, + { + "name": "ValidatorPreferences", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "ValidatorPrefs" + }, + "documentation": [ + " Preferences that a validator has." + ] + }, + { + "name": "Intentions", + "modifier": 1, + "type": "Vec", + "documentation": [ + " All the accounts with a desire to stake." + ] + }, + { + "name": "Nominating", + "modifier": 0, + "type": { + "key": "AccountId", + "value": "AccountId" + }, + "documentation": [ + " All nominator -> nominee relationships." + ] + }, + { + "name": "NominatorsFor", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Vec" + }, + "documentation": [ + " Nominators for a particular account." + ] + }, + { + "name": "CurrentNominatorsFor", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Vec" + }, + "documentation": [ + " Nominators for a particular account that is in action right now." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": 1, + "type": "Balance", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentOfflineSlash", + "modifier": 1, + "type": "Balance", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": 0, + "type": "BlockNumber", + "documentation": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "StakeRange", + "modifier": 1, + "type": "(Balance,Balance)", + "documentation": [ + " The highest and lowest staked validator slashable balances." + ] + }, + { + "name": "Bondage", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "BlockNumber" + }, + "documentation": [ + " The block at which the `who`'s funds become entirely liquid." + ] + }, + { + "name": "SlashCount", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "u32" + }, + "documentation": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": 0, + "type": "()", + "documentation": [ + " We are forcing a new era." + ] + } + ] + } + }, + { + "prefix": "democracy", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 1, + "name": "second", + "arguments": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 2, + "name": "vote", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "approve_proposal", + "type": "bool" + } + ], + "documentation": [ + " Vote in a referendum. If `approve_proposal` is true, the vote is to enact the proposal;", + " false would be a vote to keep the status quo." + ] + }, + { + "id": 3, + "name": "start_referendum", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Start a referendum." + ] + }, + { + "id": 4, + "name": "cancel_referendum", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a referendum." + ] + }, + { + "id": 5, + "name": "cancel_queued", + "arguments": [ + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "which", + "type": "u32" + } + ], + "documentation": [ + " Cancel a proposal queued for enactment." + ] + } + ] + } + }, + "storage": { + "prefix": "Democracy", + "functions": [ + { + "name": "PublicPropCount", + "modifier": 1, + "type": "PropIndex", + "documentation": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": 1, + "type": "Vec<(PropIndex,Proposal,AccountId)>", + "documentation": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": 0, + "type": { + "key": "PropIndex", + "value": "(Balance,Vec)" + }, + "documentation": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": 1, + "type": "ReferendumIndex", + "documentation": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": 1, + "type": "ReferendumIndex", + "documentation": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": 0, + "type": { + "key": "ReferendumIndex", + "value": "(ReferendumInfo)" + }, + "documentation": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": 1, + "type": { + "key": "BlockNumber", + "value": "Vec>" + }, + "documentation": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "Bondage", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "BlockNumber" + }, + "documentation": [ + " The block at which the `who`'s funds become liquid." + ] + }, + { + "name": "VotersFor", + "modifier": 1, + "type": { + "key": "ReferendumIndex", + "value": "Vec" + }, + "documentation": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": 0, + "type": { + "key": "(ReferendumIndex,AccountId)", + "value": "bool" + }, + "documentation": [ + " Get the vote, if Some, of `who`." + ] + } + ] + } + }, + { + "prefix": "grandpa", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + } + ] + } + }, + "storage": { + "prefix": "GrandpaFinality", + "functions": [ + { + "name": "PendingChange", + "modifier": 0, + "type": "StoredPendingChange", + "documentation": [] + } + ] + } + }, + { + "prefix": "council", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_approvals", + "arguments": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "id": 1, + "name": "reap_inactive_voter", + "arguments": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "id": 2, + "name": "retract_voter", + "arguments": [ + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "id": 3, + "name": "submit_candidacy", + "arguments": [ + { + "name": "slot", + "type": "Compact" + } + ], + "documentation": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "id": 4, + "name": "present_winner", + "arguments": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "id": 5, + "name": "set_desired_seats", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "id": 6, + "name": "remove_member", + "arguments": [ + { + "name": "who", + "type": "Address" + } + ], + "documentation": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "id": 7, + "name": "set_presentation_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalise_vote`." + ] + }, + { + "id": 8, + "name": "set_term_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalise_vote`." + ] + } + ] + } + }, + "storage": { + "prefix": "Council", + "functions": [ + { + "name": "CandidacyBond", + "modifier": 1, + "type": "Balance", + "documentation": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": 1, + "type": "Balance", + "documentation": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": 1, + "type": "Balance", + "documentation": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": 1, + "type": "u32", + "documentation": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": 1, + "type": "VoteIndex", + "documentation": [ + " How many votes need to go by after a voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": 1, + "type": "u32", + "documentation": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": 1, + "type": "Vec<(AccountId,BlockNumber)>", + "documentation": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters." + ] + }, + { + "name": "VoteCount", + "modifier": 1, + "type": "VoteIndex", + "documentation": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": 1, + "type": { + "key": "AccountId", + "value": "Vec" + }, + "documentation": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": 0, + "type": { + "key": "AccountId", + "value": "(VoteIndex,u32)" + }, + "documentation": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": 0, + "type": { + "key": "AccountId", + "value": "VoteIndex" + }, + "documentation": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": 1, + "type": "Vec", + "documentation": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": 1, + "type": "Vec", + "documentation": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": 1, + "type": "u32", + "documentation": [] + }, + { + "name": "NextFinalise", + "modifier": 0, + "type": "(BlockNumber,u32,Vec)", + "documentation": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": 1, + "type": "Vec", + "documentation": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": 0, + "type": "Vec<(Balance,AccountId)>", + "documentation": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ] + } + }, + { + "prefix": "council_voting", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + }, + { + "id": 2, + "name": "veto", + "arguments": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "set_cooloff_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + }, + { + "id": 4, + "name": "set_voting_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilVoting", + "functions": [ + { + "name": "CooloffPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": 1, + "type": "Vec<(BlockNumber,Hash)>", + "documentation": [] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "key": "Hash", + "value": "Proposal" + }, + "documentation": [] + }, + { + "name": "ProposalVoters", + "modifier": 1, + "type": { + "key": "Hash", + "value": "Vec" + }, + "documentation": [] + }, + { + "name": "CouncilVoteOf", + "modifier": 0, + "type": { + "key": "(Hash,AccountId)", + "value": "bool" + }, + "documentation": [] + }, + { + "name": "VetoedProposal", + "modifier": 0, + "type": { + "key": "Hash", + "value": "(BlockNumber,Vec)" + }, + "documentation": [] + } + ] + } + }, + { + "prefix": "council_motions", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilMotions", + "functions": [ + { + "name": "Proposals", + "modifier": 1, + "type": "Vec", + "documentation": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "key": "Hash", + "value": "Proposal" + }, + "documentation": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": 0, + "type": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)" + }, + "documentation": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": 1, + "type": "u32", + "documentation": [ + " Proposals so far." + ] + } + ] + } + }, + { + "prefix": "treasury", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose_spend", + "arguments": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "documentation": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "id": 1, + "name": "set_pot", + "arguments": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "documentation": [ + " Set the balance of funds available to spend." + ] + }, + { + "id": 2, + "name": "configure", + "arguments": [ + { + "name": "proposal_bond", + "type": "Permill" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Permill" + } + ], + "documentation": [ + " (Re-)configure this module." + ] + }, + { + "id": 3, + "name": "reject_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "id": 4, + "name": "approve_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ] + } + }, + "storage": { + "prefix": "Treasury", + "functions": [ + { + "name": "ProposalBond", + "modifier": 1, + "type": "Permill", + "documentation": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": 1, + "type": "Balance", + "documentation": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": 1, + "type": "BlockNumber", + "documentation": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": 1, + "type": "Permill", + "documentation": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": 1, + "type": "Balance", + "documentation": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": 1, + "type": "ProposalIndex", + "documentation": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": 0, + "type": { + "key": "ProposalIndex", + "value": "Proposal" + }, + "documentation": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": 1, + "type": "Vec", + "documentation": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + } + }, + { + "prefix": "parachains", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_heads", + "arguments": [ + { + "name": "heads", + "type": "Vec" + } + ], + "documentation": [ + " Provide candidate receipts for parachains, in ascending order by id." + ] + }, + { + "id": 1, + "name": "register_parachain", + "arguments": [ + { + "name": "id", + "type": "Id" + }, + { + "name": "code", + "type": "Bytes" + }, + { + "name": "initial_head_data", + "type": "Bytes" + } + ], + "documentation": [ + " Register a parachain with given code.", + " Fails if given ID is already used." + ] + }, + { + "id": 2, + "name": "deregister_parachain", + "arguments": [ + { + "name": "id", + "type": "Id" + } + ], + "documentation": [ + " Deregister a parachain with given id" + ] + } + ] + } + }, + "storage": { + "prefix": "Parachains", + "functions": [ + { + "name": "Parachains", + "modifier": 1, + "type": "Vec", + "documentation": [] + }, + { + "name": "Code", + "modifier": 0, + "type": { + "key": "Id", + "value": "Bytes" + }, + "documentation": [] + }, + { + "name": "Heads", + "modifier": 0, + "type": { + "key": "Id", + "value": "Bytes" + }, + "documentation": [] + }, + { + "name": "DidUpdate", + "modifier": 1, + "type": "bool", + "documentation": [] + } + ] + } + } + ], + "outerDispatch": { + "name": "Call", + "calls": [ + { + "name": "Timestamp", + "prefix": "timestamp", + "index": 0 + }, + { + "name": "Consensus", + "prefix": "consensus", + "index": 1 + }, + { + "name": "Balances", + "prefix": "balances", + "index": 2 + }, + { + "name": "Session", + "prefix": "session", + "index": 3 + }, + { + "name": "Staking", + "prefix": "staking", + "index": 4 + }, + { + "name": "Democracy", + "prefix": "democracy", + "index": 5 + }, + { + "name": "Grandpa", + "prefix": "grandpa", + "index": 6 + }, + { + "name": "Council", + "prefix": "council", + "index": 7 + }, + { + "name": "CouncilVoting", + "prefix": "council_voting", + "index": 8 + }, + { + "name": "CouncilMotions", + "prefix": "council_motions", + "index": 9 + }, + { + "name": "Treasury", + "prefix": "treasury", + "index": 10 + }, + { + "name": "Parachains", + "prefix": "parachains", + "index": 11 + } + ] + } +} diff --git a/packages/types/src/Metadata/v0/latest.substrate.json b/packages/types/src/Metadata/v0/latest.substrate.json new file mode 100644 index 0000000000..1a68c40d24 --- /dev/null +++ b/packages/types/src/Metadata/v0/latest.substrate.json @@ -0,0 +1,2898 @@ +{ + "outerEvent": { + "name": "Event", + "events": [ + [ + "system", + [ + { + "name": "ExtrinsicSuccess", + "arguments": [], + "documentation": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "arguments": [], + "documentation": [ + " An extrinsic failed." + ] + } + ] + ], + [ + "indices", + [ + { + "name": "NewAccountIndex", + "arguments": [ + "AccountId", + "AccountIndex" + ], + "documentation": [ + " A new account was created." + ] + } + ] + ], + [ + "balances", + [ + { + "name": "NewAccount", + "arguments": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "arguments": [ + "AccountId" + ], + "documentation": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "arguments": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "documentation": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + ], + [ + "session", + [ + { + "name": "NewSession", + "arguments": [ + "BlockNumber" + ], + "documentation": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + ], + [ + "staking", + [ + { + "name": "Reward", + "arguments": [ + "Balance" + ], + "documentation": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "arguments": [ + "AccountId", + "u32" + ], + "documentation": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "arguments": [ + "AccountId", + "Balance" + ], + "documentation": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + ], + [ + "democracy", + [ + { + "name": "Tabled", + "arguments": [ + "PropIndex", + "Balance", + "Vec" + ], + "documentation": [] + }, + { + "name": "Started", + "arguments": [ + "ReferendumIndex", + "VoteThreshold" + ], + "documentation": [] + }, + { + "name": "Passed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "NotPassed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Cancelled", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Executed", + "arguments": [ + "ReferendumIndex", + "bool" + ], + "documentation": [] + } + ] + ], + [ + "council", + [ + { + "name": "VoterReaped", + "arguments": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "arguments": [ + "AccountId" + ], + "documentation": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "arguments": [ + "u32" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalised", + "arguments": [ + "Vec", + "Vec" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + ], + [ + "council_voting", + [ + { + "name": "TallyCancelation", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + ], + [ + "council_motions", + [ + { + "name": "Proposed", + "arguments": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "documentation": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "arguments": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "documentation": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "arguments": [ + "Hash", + "bool" + ], + "documentation": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + ], + [ + "grandpa", + [ + { + "name": "NewAuthorities", + "arguments": [ + "Vec<(SessionKey,u64)>" + ], + "documentation": [ + " New authority set has been applied." + ] + } + ] + ], + [ + "treasury", + [ + { + "name": "Proposed", + "arguments": [ + "ProposalIndex" + ], + "documentation": [ + " New proposal." + ] + }, + { + "name": "Spending", + "arguments": [ + "Balance" + ], + "documentation": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "arguments": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "documentation": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "arguments": [ + "Balance" + ], + "documentation": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "arguments": [ + "Balance" + ], + "documentation": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + ], + [ + "contract", + [ + { + "name": "Transfer", + "arguments": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`." + ] + }, + { + "name": "Created", + "arguments": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " Contract deployed by address at the specified address." + ] + } + ] + ], + [ + "sudo", + [ + { + "name": "Sudid", + "arguments": [ + "bool" + ], + "documentation": [ + " A sudo just took place." + ] + }, + { + "name": "KeyChanged", + "arguments": [ + "AccountId" + ], + "documentation": [ + " The sudoer just switched identity; the old key is supplied." + ] + } + ] + ] + ] + }, + "modules": [ + { + "prefix": "system", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": { + "prefix": "System", + "functions": [ + { + "name": "AccountNonce", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Index" + } + }, + "default": "0x0000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "BlockHash", + "modifier": 1, + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Hash" + } + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicData", + "modifier": 1, + "type": { + "MapType": { + "key": "u32", + "value": "Bytes" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "RandomSeed", + "modifier": 1, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "Number", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": 1, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicsRoot", + "modifier": 1, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "Digest", + "modifier": 1, + "type": { + "PlainType": "Digest" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "Events", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "aura", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": null + }, + { + "prefix": "timestamp", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set", + "arguments": [ + { + "name": "now", + "type": "Compact" + } + ], + "documentation": [ + " Set the current time.", + "", + " Extrinsic with this call should be placed at the specific position in the each block", + " (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block.", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `block_period`." + ] + } + ] + } + }, + "storage": { + "prefix": "Timestamp", + "functions": [ + { + "name": "Now", + "modifier": 1, + "type": { + "PlainType": "Moment" + }, + "default": "0x0000000000000000", + "documentation": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": 1, + "type": { + "PlainType": "Moment" + }, + "default": "0x0500000000000000", + "documentation": [ + " The minimum (and advised) period between blocks." + ] + }, + { + "name": "DidUpdate", + "modifier": 1, + "type": { + "PlainType": "bool" + }, + "default": "0x00", + "documentation": [ + " Did the timestamp get updated in this block?" + ] + } + ] + } + }, + { + "prefix": "consensus", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + }, + { + "id": 1, + "name": "note_offline", + "arguments": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "documentation": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "id": 2, + "name": "remark", + "arguments": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "documentation": [ + " Make some on-chain remark." + ] + }, + { + "id": 3, + "name": "set_heap_pages", + "arguments": [ + { + "name": "pages", + "type": "u64" + } + ], + "documentation": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "id": 4, + "name": "set_code", + "arguments": [ + { + "name": "new", + "type": "Bytes" + } + ], + "documentation": [ + " Set the new code." + ] + }, + { + "id": 5, + "name": "set_storage", + "arguments": [ + { + "name": "items", + "type": "Vec" + } + ], + "documentation": [ + " Set some items of storage." + ] + } + ] + } + }, + "storage": { + "prefix": "Consensus", + "functions": [ + { + "name": "OriginalAuthorities", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "indices", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": { + "prefix": "Indices", + "functions": [ + { + "name": "NextEnumSet", + "modifier": 1, + "type": { + "PlainType": "AccountIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountIndex", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " The enumeration sets." + ] + } + ] + } + }, + { + "prefix": "balances", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "transfer", + "arguments": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Transfer some liquid free balance to another staker." + ] + }, + { + "id": 1, + "name": "set_balance", + "arguments": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "documentation": [ + " Set the balances of a given account." + ] + } + ] + } + }, + "storage": { + "prefix": "Balances", + "functions": [ + { + "name": "TotalIssuance", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The total amount of stake on the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The minimum amount allowed to keep an account open." + ] + }, + { + "name": "TransferFee", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee required to create an account. At least as big as ReclaimRebate." + ] + }, + { + "name": "FreeBalance", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It is", + " alone used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to cleanup data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable. (This is different", + " and wholly unrelated to the `Bondage` system used in the staking module.)", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + } + ] + } + }, + { + "prefix": "session", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_key", + "arguments": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "documentation": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "id": 1, + "name": "set_length", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "id": 2, + "name": "force_new_session", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Forces a new session." + ] + } + ] + } + }, + "storage": { + "prefix": "Session", + "functions": [ + { + "name": "Validators", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": 1, + "type": { + "PlainType": "Moment" + }, + "default": "0x0000000000000000", + "documentation": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": 0, + "type": { + "PlainType": "bool" + }, + "default": "0x00", + "documentation": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "SessionKey" + } + }, + "default": "0x00", + "documentation": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " The next session length." + ] + } + ] + } + }, + { + "prefix": "staking", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "stake", + "arguments": [], + "documentation": [ + " Declare the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 1, + "name": "unstake", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + } + ], + "documentation": [ + " Retract the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 2, + "name": "nominate", + "arguments": [ + { + "name": "target", + "type": "Address" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "unnominate", + "arguments": [ + { + "name": "target_index", + "type": "Compact" + } + ], + "documentation": [ + " Will panic if called when source isn't currently nominating target.", + " Updates Nominating, NominatorsFor and NominationBalance." + ] + }, + { + "id": 4, + "name": "register_preferences", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + }, + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "documentation": [ + " Set the given account's preference for slashing behaviour should they be a validator.", + "", + " An error (no-op) if `Self::intentions()[intentions_index] != origin`." + ] + }, + { + "id": 5, + "name": "set_sessions_per_era", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the number of sessions in an era." + ] + }, + { + "id": 6, + "name": "set_bonding_duration", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The length of the bonding duration in eras." + ] + }, + { + "id": 7, + "name": "set_validator_count", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The ideal number of validators." + ] + }, + { + "id": 8, + "name": "force_new_era", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "id": 9, + "name": "set_offline_slash_grace", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the offline slash grace period." + ] + }, + { + "id": 10, + "name": "set_invulnerables", + "arguments": [ + { + "name": "validators", + "type": "Vec" + } + ], + "documentation": [ + " Set the validators who cannot be slashed (if any)." + ] + } + ] + } + }, + "storage": { + "prefix": "Staking", + "functions": [ + { + "name": "ValidatorCount", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x04000000", + "documentation": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": 1, + "type": { + "PlainType": "Perbill" + }, + "default": "0x3c000000", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": 1, + "type": { + "PlainType": "Perbill" + }, + "default": "0x40420f00", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "Invulerables", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " Any validators that may never be slashed or forcible kicked. It's a Vec since they're easy to initialise", + " and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets." + ] + }, + { + "name": "CurrentEra", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The current era index." + ] + }, + { + "name": "ValidatorPreferences", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "ValidatorPrefs" + } + }, + "default": "0x0c00", + "documentation": [ + " Preferences that a validator has." + ] + }, + { + "name": "Intentions", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " All the accounts with a desire to stake." + ] + }, + { + "name": "Nominating", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountId" + } + }, + "default": "0x00", + "documentation": [ + " All nominator -> nominee relationships." + ] + }, + { + "name": "NominatorsFor", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Nominators for a particular account." + ] + }, + { + "name": "CurrentNominatorsFor", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Nominators for a particular account that is in action right now." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentOfflineSlash", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "StakeRange", + "modifier": 1, + "type": { + "PlainType": "(Balance,Balance)" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " The highest and lowest staked validator slashable balances." + ] + }, + { + "name": "Bondage", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "BlockNumber" + } + }, + "default": "0x0000000000000000", + "documentation": [ + " The block at which the `who`'s funds become entirely liquid." + ] + }, + { + "name": "SlashCount", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "u32" + } + }, + "default": "0x00000000", + "documentation": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": 0, + "type": { + "PlainType": "Null" + }, + "default": "0x00", + "documentation": [ + " We are forcing a new era." + ] + } + ] + } + }, + { + "prefix": "democracy", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 1, + "name": "second", + "arguments": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 2, + "name": "vote", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "Vote" + } + ], + "documentation": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo." + ] + }, + { + "id": 3, + "name": "start_referendum", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Start a referendum." + ] + }, + { + "id": 4, + "name": "cancel_referendum", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a referendum." + ] + }, + { + "id": 5, + "name": "cancel_queued", + "arguments": [ + { + "name": "when", + "type": "Compact" + }, + { + "name": "which", + "type": "Compact" + } + ], + "documentation": [ + " Cancel a proposal queued for enactment." + ] + } + ] + } + }, + "storage": { + "prefix": "Democracy", + "functions": [ + { + "name": "PublicPropCount", + "modifier": 1, + "type": { + "PlainType": "PropIndex" + }, + "default": "0x00000000", + "documentation": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": 1, + "type": { + "PlainType": "Vec<(PropIndex,Proposal,AccountId)>" + }, + "default": "0x00", + "documentation": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": 0, + "type": { + "MapType": { + "key": "PropIndex", + "value": "(Balance,Vec)" + } + }, + "default": "0x00", + "documentation": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "MaxLockPeriods", + "modifier": 1, + "type": { + "PlainType": "LockPeriods" + }, + "default": "0x00", + "documentation": [ + " The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`." + ] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": 1, + "type": { + "PlainType": "ReferendumIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": 1, + "type": { + "PlainType": "ReferendumIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": 0, + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "ReferendumInfo" + } + }, + "default": "0x00", + "documentation": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": 1, + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Vec>" + } + }, + "default": "0x00", + "documentation": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "Bondage", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "BlockNumber" + } + }, + "default": "0x0000000000000000", + "documentation": [ + " The block at which the `who`'s funds become liquid." + ] + }, + { + "name": "VotersFor", + "modifier": 1, + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": 1, + "type": { + "MapType": { + "key": "(ReferendumIndex,AccountId)", + "value": "Vote" + } + }, + "default": "0x00", + "documentation": [ + " Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the", + " voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check", + " `voters_for`, then you can also check for simple existence with `VoteOf::exists` first." + ] + } + ] + } + }, + { + "prefix": "council", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_approvals", + "arguments": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "id": 1, + "name": "reap_inactive_voter", + "arguments": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "id": 2, + "name": "retract_voter", + "arguments": [ + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "id": 3, + "name": "submit_candidacy", + "arguments": [ + { + "name": "slot", + "type": "Compact" + } + ], + "documentation": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "id": 4, + "name": "present_winner", + "arguments": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "id": 5, + "name": "set_desired_seats", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "id": 6, + "name": "remove_member", + "arguments": [ + { + "name": "who", + "type": "Address" + } + ], + "documentation": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "id": 7, + "name": "set_presentation_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalise_vote`." + ] + }, + { + "id": 8, + "name": "set_term_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalise_vote`." + ] + } + ] + } + }, + "storage": { + "prefix": "Council", + "functions": [ + { + "name": "CandidacyBond", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x09000000000000000000000000000000", + "documentation": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x01000000000000000000000000000000", + "documentation": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x02000000", + "documentation": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": 1, + "type": { + "PlainType": "VoteIndex" + }, + "default": "0x01000000", + "documentation": [ + " How many vote indexes need to go by after a target voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0500000000000000", + "documentation": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": 1, + "type": { + "PlainType": "Vec<(AccountId,BlockNumber)>" + }, + "default": "0x00", + "documentation": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters. The block number (second element in the tuple) is the block that their position is", + " active until (calculated by the sum of the block number when the council member was elected", + " and their term duration)." + ] + }, + { + "name": "VoteCount", + "modifier": 1, + "type": { + "PlainType": "VoteIndex" + }, + "default": "0x00000000", + "documentation": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": 1, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " A list of votes for each voter, respecting the last cleared vote index that this voter was", + " last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "(VoteIndex,u32)" + } + }, + "default": "0x00", + "documentation": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "VoteIndex" + } + }, + "default": "0x00", + "documentation": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [] + }, + { + "name": "NextFinalise", + "modifier": 0, + "type": { + "PlainType": "(BlockNumber,u32,Vec)" + }, + "default": "0x00", + "documentation": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": 0, + "type": { + "PlainType": "Vec<(Balance,AccountId)>" + }, + "default": "0x00", + "documentation": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ] + } + }, + { + "prefix": "council_voting", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + }, + { + "id": 2, + "name": "veto", + "arguments": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "set_cooloff_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + }, + { + "id": 4, + "name": "set_voting_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilVoting", + "functions": [ + { + "name": "CooloffPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [] + }, + { + "name": "VotingPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0300000000000000", + "documentation": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": 1, + "type": { + "PlainType": "Vec<(BlockNumber,Hash)>" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "ProposalVoters", + "modifier": 1, + "type": { + "MapType": { + "key": "Hash", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "CouncilVoteOf", + "modifier": 0, + "type": { + "MapType": { + "key": "(Hash,AccountId)", + "value": "bool" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "VetoedProposal", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "(BlockNumber,Vec)" + } + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "council_motions", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilMotions", + "functions": [ + { + "name": "Proposals", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)" + } + }, + "default": "0x00", + "documentation": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": 1, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Proposals so far." + ] + } + ] + } + }, + { + "prefix": "grandpa", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + } + ] + } + }, + "storage": { + "prefix": "GrandpaFinality", + "functions": [ + { + "name": "PendingChange", + "modifier": 0, + "type": { + "PlainType": "StoredPendingChange" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "treasury", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose_spend", + "arguments": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "documentation": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "id": 1, + "name": "set_pot", + "arguments": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "documentation": [ + " Set the balance of funds available to spend." + ] + }, + { + "id": 2, + "name": "configure", + "arguments": [ + { + "name": "proposal_bond", + "type": "Permill" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Permill" + } + ], + "documentation": [ + " (Re-)configure this module." + ] + }, + { + "id": 3, + "name": "reject_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "id": 4, + "name": "approve_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ] + } + }, + "storage": { + "prefix": "Treasury", + "functions": [ + { + "name": "ProposalBond", + "modifier": 1, + "type": { + "PlainType": "Permill" + }, + "default": "0x00000000", + "documentation": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": 1, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0100000000000000", + "documentation": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": 1, + "type": { + "PlainType": "Permill" + }, + "default": "0x00000000", + "documentation": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": 1, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": 1, + "type": { + "PlainType": "ProposalIndex" + }, + "default": "0x00000000", + "documentation": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": 0, + "type": { + "MapType": { + "key": "ProposalIndex", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": 1, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + } + }, + { + "prefix": "contract", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "call", + "arguments": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "documentation": [ + " Make a call to a specified account, optionally transferring some balance.", + " Make a call to a specified account, optionally transferring some balance." + ] + }, + { + "id": 1, + "name": "create", + "arguments": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "ctor_code", + "type": "Bytes" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "documentation": [ + " Create a new contract, optionally transfering some balance to the created account.", + "", + " Creation is executed as follows:", + "", + " - the destination address is computed based on the sender and hash of the code.", + " - account is created at the computed address.", + " - the `ctor_code` is executed in the context of the newly created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any message received by this account." + ] + } + ] + } + }, + "storage": null + }, + { + "prefix": "sudo", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "sudo", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "set_key", + "arguments": [ + { + "name": "new", + "type": "Address" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "Sudo", + "functions": [ + { + "name": "Key", + "modifier": 1, + "type": { + "PlainType": "AccountId" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + } + ] + } + } + ], + "outerDispatch": { + "name": "Call", + "calls": [ + { + "name": "Timestamp", + "prefix": "timestamp", + "index": 0 + }, + { + "name": "Consensus", + "prefix": "consensus", + "index": 1 + }, + { + "name": "Indices", + "prefix": "indices", + "index": 2 + }, + { + "name": "Balances", + "prefix": "balances", + "index": 3 + }, + { + "name": "Session", + "prefix": "session", + "index": 4 + }, + { + "name": "Staking", + "prefix": "staking", + "index": 5 + }, + { + "name": "Democracy", + "prefix": "democracy", + "index": 6 + }, + { + "name": "Council", + "prefix": "council", + "index": 7 + }, + { + "name": "CouncilVoting", + "prefix": "council_voting", + "index": 8 + }, + { + "name": "CouncilMotions", + "prefix": "council_motions", + "index": 9 + }, + { + "name": "Grandpa", + "prefix": "grandpa", + "index": 10 + }, + { + "name": "Treasury", + "prefix": "treasury", + "index": 11 + }, + { + "name": "Contract", + "prefix": "contract", + "index": 12 + }, + { + "name": "Sudo", + "prefix": "sudo", + "index": 13 + } + ] + } +} diff --git a/packages/types/src/Metadata/v0/static.ts b/packages/types/src/Metadata/v0/static.ts new file mode 100644 index 0000000000..b7a7edeb78 --- /dev/null +++ b/packages/types/src/Metadata/v0/static.ts @@ -0,0 +1,8 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const meta = '0x144576656e74341873797374656d084045787472696e7369635375636365737300049420416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656400045420416e2065787472696e736963206661696c65642e1c696e6469636573043c4e65774163636f756e74496e64657808244163636f756e744964304163636f756e74496e646578046c2041206e6577206163636f756e742077617320637265617465642e2062616c616e6365730c284e65774163636f756e7408244163636f756e7449641c42616c616e6365046c2041206e6577206163636f756e742077617320637265617465642e345265617065644163636f756e7404244163636f756e744964045c20416e206163636f756e7420776173207265617065642e205472616e7366657210244163636f756e744964244163636f756e7449641c42616c616e63651c42616c616e636504b0205472616e7366657220737563636565646564202866726f6d2c20746f2c2076616c75652c2066656573292e1c73657373696f6e04284e657753657373696f6e042c426c6f636b4e756d626572085501204e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f742074686520626c6f636b88206e756d626572206173207468652074797065206d6967687420737567676573742e1c7374616b696e670c18526577617264041c42616c616e636504e020416c6c2076616c696461746f72732068617665206265656e2072657761726465642062792074686520676976656e2062616c616e63652e384f66666c696e655761726e696e6708244163636f756e7449640c753332085501204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20676976656e2061206f66666c696e652d7761726e696e67202874686579277265207374696c6c15012077697468696e207468656972206772616365292e205468652061636372756564206e756d626572206f6620736c6173686573206973207265636f726465642c20746f6f2e304f66666c696e65536c61736808244163636f756e7449641c42616c616e6365042d01204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20736c61736865642062792074686520676976656e20616d6f756e742e2464656d6f637261637918185461626c65640c2450726f70496e6465781c42616c616e6365385665633c4163636f756e7449643e001c53746172746564083c5265666572656e64756d496e64657834566f74655468726573686f6c640018506173736564043c5265666572656e64756d496e64657800244e6f74506173736564043c5265666572656e64756d496e646578002443616e63656c6c6564043c5265666572656e64756d496e64657800204578656375746564083c5265666572656e64756d496e64657810626f6f6c001c636f756e63696c102c566f74657252656170656408244163636f756e744964244163636f756e74496404542072656170656420766f7465722c2072656170657240426164526561706572536c617368656404244163636f756e744964043c20736c6173686564207265617065723054616c6c7953746172746564040c75333204f420412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320737461727465642e3854616c6c7946696e616c6973656408385665633c4163636f756e7449643e385665633c4163636f756e7449643e04690120412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320656e646564202877697468206f6e65206f72206d6f7265206e6577206d656d62657273292e38636f756e63696c5f766f74696e67084054616c6c7943616e63656c6174696f6e1010486173680c7533320c7533320c753332080101204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d2063616e63656c6c6174696f6e20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c54616c6c795265666572656e64756d1010486173680c7533320c7533320c75333208cc204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c636f756e63696c5f6d6f74696f6e73142050726f706f73656410244163636f756e7449643450726f706f73616c496e64657810486173680c7533320465012041206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e20753332292e14566f74656414244163636f756e744964104861736810626f6f6c0c7533320c7533320809012041206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e67f420612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e206173207533327320726573706563746976656c79292e20417070726f76656404104861736804c42041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404104861736804d42041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408104861736810626f6f6c0405012041206d6f74696f6e207761732065786563757465643b2060626f6f6c6020697320747275652069662072657475726e656420776974686f7574206572726f722e1c6772616e64706104384e6577417574686f72697469657304585665633c2853657373696f6e4b65792c20753634293e0490204e657720617574686f726974792073657420686173206265656e206170706c6965642e207472656173757279142050726f706f736564043450726f706f73616c496e6465780438204e65772070726f706f73616c2e205370656e64696e67041c42616c616e636504e8205765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c3450726f706f73616c496e6465781c42616c616e6365244163636f756e744964048020536f6d652066756e64732068617665206265656e20616c6c6f63617465642e144275726e74041c42616c616e6365048c20536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572041c42616c616e6365043101205370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e20636f6e747261637408205472616e736665720c244163636f756e744964244163636f756e7449641c42616c616e6365047501205472616e736665722068617070656e6564206066726f6d60202d3e2060746f60207769746820676976656e206076616c7565602061732070617274206f66206120606d6573736167652d63616c6c60206f722060637265617465602e1c4372656174656408244163636f756e744964244163636f756e74496404dc20436f6e7472616374206465706c6f7965642062792061646472657373206174207468652073706563696669656420616464726573732e107375646f081453756469640410626f6f6c04602041207375646f206a75737420746f6f6b20706c6163652e284b65794368616e67656404244163636f756e74496404f020546865207375646f6572206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c6965642e401873797374656d184d6f64756c651043616c6c00011853797374656d28304163636f756e744e6f6e6365010130543a3a4163636f756e74496420543a3a496e646578200000000000000000003845787472696e736963436f756e7400000c75333204000024426c6f636b48617368010138543a3a426c6f636b4e756d6265721c543a3a48617368800000000000000000000000000000000000000000000000000000000000000000003445787472696e7369634461746101010c7533321c5665633c75383e0400002852616e646f6d5365656401001c543a3a4861736880000000000000000000000000000000000000000000000000000000000000000000184e756d626572010038543a3a426c6f636b4e756d626572200000000000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801001c543a3a48617368800000000000000000000000000000000000000000000000000000000000000000003845787472696e73696373526f6f7401001c543a3a486173688000000000000000000000000000000000000000000000000000000000000000000018446967657374010024543a3a446967657374040000184576656e74730100685665633c4576656e745265636f72643c543a3a4576656e743e3e0400001061757261184d6f64756c651043616c6c00002474696d657374616d70184d6f64756c651043616c6c0400000c736574040c6e6f777c3c543a3a4d6f6d656e7420617320486173436f6d706163743e3a3a54797065205820536574207468652063757272656e742074696d652e0055012045787472696e736963207769746820746869732063616c6c2073686f756c6420626520706c616365642061742074686520737065636966696320706f736974696f6e20696e20746865206561636820626c6f636b6d012028737065636966696564206279207468652054726169743a3a54494d455354414d505f5345545f504f534954494f4e29207479706963616c6c7920617420746865207374617274206f6620746865206561636820626c6f636b2e750120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e2070686173652cbc20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e008501205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e74207370656369666965642062792060626c6f636b5f706572696f64602e012454696d657374616d700c0c4e6f77010024543a3a4d6f6d656e7420000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2c426c6f636b506572696f64010024543a3a4d6f6d656e7420050000000000000004c420546865206d696e696d756d2028616e6420616476697365642920706572696f64206265747765656e20626c6f636b732e24446964557064617465010010626f6f6c040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f24636f6e73656e737573184d6f64756c651043616c6c180000487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e0100306e6f74655f6f66666c696e65041c6f66666c696e65f43c543a3a496e686572656e744f66666c696e655265706f727420617320496e686572656e744f66666c696e655265706f72743e3a3a496e686572656e74044501204e6f7465207468652070726576696f757320626c6f636b27732076616c696461746f72206d6973736564207468656972206f70706f7274756e69747920746f2070726f706f7365206120626c6f636b2e02001872656d61726b041c5f72656d61726b1c5665633c75383e046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e0300387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e0400207365745f636f6465040c6e65771c5665633c75383e04482053657420746865206e657720636f64652e05002c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e0124436f6e73656e737573044c4f726967696e616c417574686f7269746965730000485665633c543a3a53657373696f6e4b65793e0400001c696e6469636573184d6f64756c651043616c6c00011c496e6469636573082c4e657874456e756d53657401003c543a3a4163636f756e74496e6465781000000000047c20546865206e657874206672656520656e756d65726174696f6e207365742e1c456e756d53657401013c543a3a4163636f756e74496e646578445665633c543a3a4163636f756e7449643e040004582054686520656e756d65726174696f6e20736574732e2062616c616e636573184d6f64756c651043616c6c080000207472616e736665720810646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c7565803c543a3a42616c616e636520617320486173436f6d706163743e3a3a5479706504d4205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572207374616b65722e01002c7365745f62616c616e63650c0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651066726565803c543a3a42616c616e636520617320486173436f6d706163743e3a3a54797065207265736572766564803c543a3a42616c616e636520617320486173436f6d706163743e3a3a54797065049420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e012042616c616e6365732034546f74616c49737375616e6365010028543a3a42616c616e6365400000000000000000000000000000000004a42054686520746f74616c20616d6f756e74206f66207374616b65206f6e207468652073797374656d2e484578697374656e7469616c4465706f736974010028543a3a42616c616e6365400000000000000000000000000000000004d020546865206d696e696d756d20616d6f756e7420616c6c6f77656420746f206b65657020616e206163636f756e74206f70656e2e2c5472616e73666572466565010028543a3a42616c616e636540000000000000000000000000000000000494205468652066656520726571756972656420746f206d616b652061207472616e736665722e2c4372656174696f6e466565010028543a3a42616c616e63654000000000000000000000000000000000042501205468652066656520726571756972656420746f2063726561746520616e206163636f756e742e204174206c6561737420617320626967206173205265636c61696d5265626174652e2c4672656542616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e636540000000000000000000000000000000002c9c20546865202766726565272062616c616e6365206f66206120676976656e206163636f756e742e004d01205468697320697320746865206f6e6c792062616c616e63652074686174206d61747465727320696e207465726d73206f66206d6f7374206f7065726174696f6e73206f6e20746f6b656e732e204974206973690120616c6f6e65207573656420746f2064657465726d696e65207468652062616c616e6365207768656e20696e2074686520636f6e747261637420657865637574696f6e20656e7669726f6e6d656e742e205768656e207468697355012062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e20746865202763757272656e74206163636f756e74272069733d012064656c657465643a207370656369666963616c6c7920604672656542616c616e6365602e20467572746865726d6f72652c20604f6e4672656542616c616e63655a65726f602063616c6c6261636b410120697320696e766f6b65642c20676976696e672061206368616e636520746f2065787465726e616c206d6f64756c657320746f20636c65616e75702064617461206173736f636961746564207769746854207468652064656c65746564206163636f756e742e005d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c657465642069662060526573657276656442616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e3c526573657276656442616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e636540000000000000000000000000000000003075012054686520616d6f756e74206f66207468652062616c616e6365206f66206120676976656e206163636f756e7420746861742069732065787465726e616c6c792072657365727665643b20746869732063616e207374696c6c206765749c20736c61736865642c20627574206765747320736c6173686564206c617374206f6620616c6c2e006d0120546869732062616c616e63652069732061202772657365727665272062616c616e63652074686174206f746865722073756273797374656d732075736520696e206f7264657220746f2073657420617369646520746f6b656e737101207468617420617265207374696c6c20276f776e65642720627920746865206163636f756e7420686f6c6465722c20627574207768696368206172652073757370656e6461626c652e20285468697320697320646966666572656e74290120616e642077686f6c6c7920756e72656c6174656420746f207468652060426f6e64616765602073797374656d207573656420696e20746865207374616b696e67206d6f64756c652e29007501205768656e20746869732062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e2074686973202772657365727665206163636f756e7427b42069732064656c657465643a207370656369666963616c6c792c2060526573657276656442616c616e6365602e004d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c6574656420696620604672656542616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e485472616e73616374696f6e42617365466565010028543a3a42616c616e6365400000000000000000000000000000000004dc205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b2074686520626173652e485472616e73616374696f6e42797465466565010028543a3a42616c616e63654000000000000000000000000000000000040d01205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b20746865207065722d6279746520706f7274696f6e2e1c73657373696f6e184d6f64756c651043616c6c0c00001c7365745f6b6579040c6b657934543a3a53657373696f6e4b65790861012053657473207468652073657373696f6e206b6579206f6620605f76616c696461746f726020746f20605f6b6579602e205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e657874242073657373696f6e2e0100287365745f6c656e677468040c6e6577903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a54797065046d01205365742061206e65772073657373696f6e206c656e6774682e20576f6e2774206b69636b20696e20756e74696c20746865206e6578742073657373696f6e206368616e6765202861742063757272656e74206c656e677468292e020044666f7263655f6e65775f73657373696f6e04346170706c795f7265776172647310626f6f6c045820466f726365732061206e65772073657373696f6e2e011c53657373696f6e202856616c696461746f72730100445665633c543a3a4163636f756e7449643e0400047c205468652063757272656e7420736574206f662076616c696461746f72732e3453657373696f6e4c656e677468010038543a3a426c6f636b4e756d62657220e803000000000000047c2043757272656e74206c656e677468206f66207468652073657373696f6e2e3043757272656e74496e646578010038543a3a426c6f636b4e756d62657220000000000000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e3043757272656e745374617274010024543a3a4d6f6d656e7420000000000000000004a02054696d657374616d70207768656e2063757272656e742073657373696f6e20737461727465642e44466f7263696e674e657753657373696f6e000010626f6f6c0400087901204e65772073657373696f6e206973206265696e6720666f72636564206973207468697320656e747279206578697374733b20696e20776869636820636173652c2074686520626f6f6c65616e2076616c75652069732077686574686572810120746865206e65772073657373696f6e2073686f756c6420626520636f6e736964657265642061206e6f726d616c20726f746174696f6e202872657761726461626c6529206f7220657863657074696f6e616c2028736c61736861626c65292e404c6173744c656e6774684368616e6765000038543a3a426c6f636b4e756d626572040004c020426c6f636b206174207768696368207468652073657373696f6e206c656e677468206c617374206368616e6765642e284e6578744b6579466f72000130543a3a4163636f756e74496434543a3a53657373696f6e4b65790400049020546865206e657874206b657920666f72206120676976656e2076616c696461746f722e444e65787453657373696f6e4c656e677468000038543a3a426c6f636b4e756d6265720400046420546865206e6578742073657373696f6e206c656e6774682e1c7374616b696e67184d6f64756c651043616c6c2c0000147374616b65000cc0204465636c617265207468652064657369726520746f207374616b6520666f7220746865207472616e736163746f722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e01001c756e7374616b650440696e74656e74696f6e735f696e64657830436f6d706163743c7533323e0cc02052657472616374207468652064657369726520746f207374616b6520666f7220746865207472616e736163746f722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e0200206e6f6d696e61746504187461726765748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636500030028756e6e6f6d696e61746504307461726765745f696e64657830436f6d706163743c7533323e0811012057696c6c2070616e69632069662063616c6c6564207768656e20736f757263652069736e27742063757272656e746c79206e6f6d696e6174696e67207461726765742ee42055706461746573204e6f6d696e6174696e672c204e6f6d696e61746f7273466f7220616e64204e6f6d696e6174696f6e42616c616e63652e04005072656769737465725f707265666572656e6365730840696e74656e74696f6e735f696e64657830436f6d706163743c7533323e1470726566736856616c696461746f7250726566733c543a3a42616c616e63653e0c5901205365742074686520676976656e206163636f756e74277320707265666572656e636520666f7220736c617368696e67206265686176696f75722073686f756c64207468657920626520612076616c696461746f722e00190120416e206572726f7220286e6f2d6f7029206966206053656c663a3a696e74656e74696f6e7328295b696e74656e74696f6e735f696e6465785d20213d206f726967696e602e0500507365745f73657373696f6e735f7065725f657261040c6e6577903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a5479706504982053657420746865206e756d626572206f662073657373696f6e7320696e20616e206572612e0600507365745f626f6e64696e675f6475726174696f6e040c6e6577903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a5479706504b020546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20657261732e07004c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e080034666f7263655f6e65775f65726104346170706c795f7265776172647310626f6f6c083d0120466f72636520746865726520746f2062652061206e6577206572612e205468697320616c736f20666f726365732061206e65772073657373696f6e20696d6d6564696174656c792061667465722e250120606170706c795f72657761726473602073686f756c64206265207472756520666f722076616c696461746f727320746f20676574207468652073657373696f6e207265776172642e09005c7365745f6f66666c696e655f736c6173685f6772616365040c6e657730436f6d706163743c7533323e04902053657420746865206f66666c696e6520736c61736820677261636520706572696f642e0a00447365745f696e76756c6e657261626c6573042876616c696461746f7273445665633c543a3a4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e011c5374616b696e67583856616c696461746f72436f756e7401000c753332100000000004a82054686520696465616c206e756d626572206f66207374616b696e67207061727469636970616e74732e544d696e696d756d56616c696461746f72436f756e7401000c7533321004000000044101204d696e696d756d206e756d626572206f66207374616b696e67207061727469636970616e7473206265666f726520656d657267656e637920636f6e646974696f6e732061726520696d706f7365642e3853657373696f6e73506572457261010038543a3a426c6f636b4e756d62657220e80300000000000004a420546865206c656e677468206f662061207374616b696e672065726120696e2073657373696f6e732e3453657373696f6e52657761726401001c50657262696c6c103c000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e304f66666c696e65536c61736801001c50657262696c6c1040420f0004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e444f66666c696e65536c617368477261636501000c7533321000000000043901204e756d626572206f6620696e7374616e636573206f66206f66666c696e65207265706f727473206265666f726520736c617368696e6720626567696e7320666f722076616c696461746f72732e3c426f6e64696e674475726174696f6e010038543a3a426c6f636b4e756d62657220e80300000000000004b820546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20626c6f636b732e30496e76756c657261626c65730100445665633c543a3a4163636f756e7449643e040008a50120416e792076616c696461746f72732074686174206d6179206e6576657220626520736c6173686564206f7220666f726369626c65206b69636b65642e20497427732061205665632073696e63652074686579277265206561737920746f20696e697469616c697365ad0120616e642074686520706572666f726d616e636520686974206973206d696e696d616c2028776520657870656374206e6f206d6f7265207468616e20666f757220696e76756c6e657261626c65732920616e64207265737472696374656420746f20746573746e6574732e2843757272656e74457261010038543a3a426c6f636b4e756d626572200000000000000000045c205468652063757272656e742065726120696e6465782e5056616c696461746f72507265666572656e636573010130543a3a4163636f756e7449646856616c696461746f7250726566733c543a3a42616c616e63653e080c00048820507265666572656e636573207468617420612076616c696461746f72206861732e28496e74656e74696f6e730100445665633c543a3a4163636f756e7449643e040004a420416c6c20746865206163636f756e7473207769746820612064657369726520746f207374616b652e284e6f6d696e6174696e67000130543a3a4163636f756e74496430543a3a4163636f756e744964040004a020416c6c206e6f6d696e61746f72202d3e206e6f6d696e65652072656c6174696f6e73686970732e344e6f6d696e61746f7273466f72010130543a3a4163636f756e744964445665633c543a3a4163636f756e7449643e04000494204e6f6d696e61746f727320666f72206120706172746963756c6172206163636f756e742e5043757272656e744e6f6d696e61746f7273466f72010130543a3a4163636f756e744964445665633c543a3a4163636f756e7449643e0400040501204e6f6d696e61746f727320666f72206120706172746963756c6172206163636f756e74207468617420697320696e20616374696f6e207269676874206e6f772e5043757272656e7453657373696f6e526577617264010028543a3a42616c616e63654000000000000000000000000000000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e4c43757272656e744f66666c696e65536c617368010028543a3a42616c616e6365400000000000000000000000000000000004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e484e65787453657373696f6e73506572457261000038543a3a426c6f636b4e756d6265720400049020546865206e6578742076616c7565206f662073657373696f6e7320706572206572612e4c4c6173744572614c656e6774684368616e6765010038543a3a426c6f636b4e756d62657220000000000000000004e0205468652073657373696f6e20696e6465782061742077686963682074686520657261206c656e677468206c617374206368616e6765642e285374616b6552616e6765010048506169724f663c543a3a42616c616e63653e80000000000000000000000000000000000000000000000000000000000000000004f020546865206869676865737420616e64206c6f77657374207374616b65642076616c696461746f7220736c61736861626c652062616c616e6365732e1c426f6e64616765010130543a3a4163636f756e74496438543a3a426c6f636b4e756d62657220000000000000000004f42054686520626c6f636b20617420776869636820746865206077686f6027732066756e6473206265636f6d6520656e746972656c79206c69717569642e28536c617368436f756e74010130543a3a4163636f756e7449640c753332100000000004d10120546865206e756d626572206f662074696d6573206120676976656e2076616c696461746f7220686173206265656e207265706f72746564206f66666c696e652e205468697320676574732064656372656d656e746564206279206f6e652065616368206572612074686174207061737365732e34466f7263696e674e65774572610000082829040004682057652061726520666f7263696e672061206e6577206572612e2464656d6f6372616379184d6f64756c651043616c6c1800001c70726f706f7365082070726f706f73616c40426f783c543a3a50726f706f73616c3e1476616c7565803c543a3a42616c616e636520617320486173436f6d706163743e3a3a5479706504a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e0100187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e020010766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f746508350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e03004073746172745f7265666572656e64756d0c2070726f706f73616c40426f783c543a3a50726f706f73616c3e247468726573686f6c6434566f74655468726573686f6c641464656c617938543a3a426c6f636b4e756d62657204502053746172742061207265666572656e64756d2e04004463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e05003463616e63656c5f71756575656408107768656e903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a5479706514776869636830436f6d706163743c7533323e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e012444656d6f63726163793c3c5075626c696350726f70436f756e7401002450726f70496e646578100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100ac5665633c2850726f70496e6465782c20543a3a50726f706f73616c2c20543a3a4163636f756e744964293e0400048020546865207075626c69632070726f706f73616c732e20556e736f727465642e244465706f7369744f6600012450726f70496e6465787c28543a3a42616c616e63652c205665633c543a3a4163636f756e7449643e29040004842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e304c61756e6368506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e384d696e696d756d4465706f736974010028543a3a42616c616e6365400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e2c5075626c696344656c6179010038543a3a426c6f636b4e756d62657220000000000000000004d4205468652064656c6179206265666f726520656e6163746d656e7420666f7220616c6c207075626c6963207265666572656e64612e384d61784c6f636b506572696f647301002c4c6f636b506572696f6473040004d90120546865206d6178696d756d206e756d626572206f66206164646974696f6e616c206c6f636b20706572696f6473206120766f746572206d6179206f6666657220746f20737472656e677468656e20746865697220766f74652e204d756c7469706c6573206f6620605075626c696344656c6179602e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e3c5265666572656e64756d436f756e7401003c5265666572656e64756d496e646578100000000004390120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e64756d73207374617274656420736f206661722e244e65787454616c6c7901003c5265666572656e64756d496e646578100000000004c820546865206e657874207265666572656e64756d20696e64657820746861742073686f756c642062652074616c6c6965642e405265666572656e64756d496e666f4f6600013c5265666572656e64756d496e646578b4285265666572656e64756d496e666f3c543a3a426c6f636b4e756d6265722c20543a3a50726f706f73616c3e29040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e3444697370617463685175657565010138543a3a426c6f636b4e756d626572ac5665633c4f7074696f6e3c28543a3a50726f706f73616c2c205265666572656e64756d496e646578293e3e040004c0205175657565206f66207375636365737366756c207265666572656e646120746f20626520646973706174636865642e1c426f6e64616765010130543a3a4163636f756e74496438543a3a426c6f636b4e756d62657220000000000000000004d02054686520626c6f636b20617420776869636820746865206077686f6027732066756e6473206265636f6d65206c69717569642e24566f74657273466f7201013c5265666572656e64756d496e646578445665633c543a3a4163636f756e7449643e040004a4204765742074686520766f7465727320666f72207468652063757272656e742070726f706f73616c2e18566f74654f6601017c285265666572656e64756d496e6465782c20543a3a4163636f756e7449642910566f746504000cd501204765742074686520766f746520696e206120676976656e207265666572656e64756d206f66206120706172746963756c617220766f7465722e2054686520726573756c74206973206d65616e696e6766756c206f6e6c792069662060766f746572735f666f726020696e636c7564657320746865c90120766f746572207768656e2063616c6c6564207769746820746865207265666572656e64756d2028796f75276c6c20676574207468652064656661756c742060566f7465602076616c7565206f7468657277697365292e20496620796f7520646f6e27742077616e7420746f20636865636b61012060766f746572735f666f72602c207468656e20796f752063616e20616c736f20636865636b20666f722073696d706c65206578697374656e636520776974682060566f74654f663a3a657869737473602066697273742e1c636f756e63696c184d6f64756c651043616c6c240000347365745f617070726f76616c730814766f746573245665633c626f6f6c3e14696e64657848436f6d706163743c566f7465496e6465783e086101205365742063616e64696461746520617070726f76616c732e20417070726f76616c20736c6f747320737461792076616c6964206173206c6f6e672061732063616e6469646174657320696e2074686f736520736c6f7473402061726520726567697374657265642e01004c726561705f696e6163746976655f766f74657210387265706f727465725f696e64657830436f6d706163743c7533323e0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263652477686f5f696e64657830436f6d706163743c7533323e48617373756d65645f766f74655f696e64657848436f6d706163743c566f7465496e6465783e1461012052656d6f7665206120766f7465722e20466f72206974206e6f7420746f206265206120626f6e642d636f6e73756d696e67206e6f2d6f702c20616c6c20617070726f7665642063616e64696461746520696e64696365737101206d757374206e6f772062652065697468657220756e72656769737465726564206f72207265676973746572656420746f20612063616e646964617465207468617420726567697374657265642074686520736c6f74206166746572a02074686520766f7465722067617665207468656972206c61737420617070726f76616c207365742e000101204d61792062652063616c6c656420627920616e796f6e652e2052657475726e732074686520766f746572206465706f73697420746f20607369676e6564602e020034726574726163745f766f7465720414696e64657830436f6d706163743c7533323e042d012052656d6f7665206120766f7465722e20416c6c20766f746573206172652063616e63656c6c656420616e642074686520766f746572206465706f7369742069732072657475726e65642e0300407375626d69745f63616e6469646163790410736c6f7430436f6d706163743c7533323e0c78205375626d6974206f6e6573656c6620666f722063616e6469646163792e001101204163636f756e74206d757374206861766520656e6f756768207472616e736665727261626c652066756e647320696e20697420746f207061792074686520626f6e642e04003870726573656e745f77696e6e65720c2463616e6469646174658c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514746f74616c803c543a3a42616c616e636520617320486173436f6d706163743e3a3a5479706514696e64657848436f6d706163743c566f7465496e6465783e0c650120436c61696d207468617420607369676e656460206973206f6e65206f662074686520746f702053656c663a3a63617272795f636f756e742829202b2063757272656e745f766f746528292e312063616e646964617465732ea501204f6e6c7920776f726b73206966207468652060626c6f636b5f6e756d626572203e3d2063757272656e745f766f746528292e306020616e6420603c2063757272656e745f766f746528292e30202b2070726573656e746174696f6e5f6475726174696f6e282960607820607369676e6564602073686f756c642068617665206174206c656173740500447365745f646573697265645f73656174730414636f756e7430436f6d706163743c7533323e0c650120536574207468652064657369726564206d656d62657220636f756e743b206966206c6f776572207468616e207468652063757272656e7420636f756e742c207468656e2073656174732077696c6c206e6f74206265207570690120656c656374696f6e207768656e2074686579206578706972652e204966206d6f72652c207468656e2061206e657720766f74652077696c6c2062652073746172746564206966206f6e65206973206e6f7420616c72656164793420696e2070726f67726573732e06003472656d6f76655f6d656d626572040c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c71012052656d6f7665206120706172746963756c6172206d656d6265722e20412074616c6c792077696c6c2068617070656e20696e7374616e746c7920286966206e6f7420616c726561647920696e20612070726573656e746174696f6e410120706572696f642920746f2066696c6c2074686520736561742069662072656d6f76616c206d65616e732074686174207468652064657369726564206d656d6265727320617265206e6f74206d65742e7c20546869732069732065666665637469766520696d6d6564696174656c792e0700647365745f70726573656e746174696f6e5f6475726174696f6e0414636f756e74903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a5479706508590120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e746c79206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e0800447365745f7465726d5f6475726174696f6e0414636f756e74903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a5479706508510120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e74206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e011c436f756e63696c503443616e646964616379426f6e64010028543a3a42616c616e6365400900000000000000000000000000000004050120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f207375626d6974206f6e6527732063616e6469646163792e28566f74696e67426f6e64010028543a3a42616c616e6365400000000000000000000000000000000004090120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f2062652061626c6520746f207375626d697420766f7465732e5050726573656e74536c617368506572566f746572010028543a3a42616c616e63654001000000000000000000000000000000040d01205468652070756e6973686d656e742c2070657220766f7465722c20696620796f752070726f7669646520616e20696e76616c69642070726573656e746174696f6e2e284361727279436f756e7401000c753332100200000004350120486f77206d616e792072756e6e6572732d75702073686f756c64206861766520746865697220617070726f76616c73207065727369737420756e74696c20746865206e65787420766f74652e5050726573656e746174696f6e4475726174696f6e010038543a3a426c6f636b4e756d62657220e803000000000000043d0120486f77206c6f6e6720746f2067697665206561636820746f702063616e64696461746520746f2070726573656e74207468656d73656c7665732061667465722074686520766f746520656e64732e4c496e6163746976654772616365506572696f64010024566f7465496e646578100100000008a10120486f77206d616e7920766f746520696e6465786573206e65656420746f20676f20627920616674657220612074617267657420766f7465722773206c61737420766f7465206265666f726520746865792063616e206265207265617065642069662074686569725020617070726f76616c7320617265206d6f6f742e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e305465726d4475726174696f6e010038543a3a426c6f636b4e756d626572200500000000000000049820486f77206c6f6e67206561636820706f736974696f6e2069732061637469766520666f722e3044657369726564536561747301000c753332100000000004e8204e756d626572206f66206163636f756e747320746861742073686f756c642062652073697474696e67206f6e2074686520636f756e63696c2e34416374697665436f756e63696c01008c5665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d626572293e0400106d01205468652063757272656e7420636f756e63696c2e205768656e2074686572652773206120766f746520676f696e67206f6e2c20746869732073686f756c64207374696c6c206265207573656420666f72206578656375746976657101206d6174746572732e2054686520626c6f636b206e756d62657220287365636f6e6420656c656d656e7420696e20746865207475706c65292069732074686520626c6f636b207468617420746865697220706f736974696f6e20697371012061637469766520756e74696c202863616c63756c61746564206279207468652073756d206f662074686520626c6f636b206e756d626572207768656e2074686520636f756e63696c206d656d6265722077617320656c65637465646820616e64207468656972207465726d206475726174696f6e292e24566f7465436f756e74010024566f7465496e64657810000000000405012054686520746f74616c206e756d626572206f6620766f746573207468617420686176652068617070656e6564206f722061726520696e2070726f67726573732e2c417070726f76616c734f66010130543a3a4163636f756e744964245665633c626f6f6c3e0400086d012041206c697374206f6620766f74657320666f72206561636820766f7465722c2072657370656374696e6720746865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f7465722077617340206c617374206163746976652061742e385265676973746572496e666f4f66000130543a3a4163636f756e7449644028566f7465496e6465782c20753332290400087d012054686520766f746520696e64657820616e64206c69737420736c6f742074686174207468652063616e646964617465206077686f60207761732072656769737465726564206f7220604e6f6e6560206966207468657920617265206e6f74582063757272656e746c7920726567697374657265642e304c6173744163746976654f66000130543a3a4163636f756e74496424566f7465496e646578040004010120546865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f74657220776173206c617374206163746976652061742e18566f746572730100445665633c543a3a4163636f756e7449643e04000460205468652070726573656e7420766f746572206c6973742e2843616e646964617465730100445665633c543a3a4163636f756e7449643e04000470205468652070726573656e742063616e646964617465206c6973742e3843616e646964617465436f756e7401000c753332100000000000304e65787446696e616c6973650000a028543a3a426c6f636b4e756d6265722c207533322c205665633c543a3a4163636f756e7449643e29040004210120546865206163636f756e747320686f6c64696e672074686520736561747320746861742077696c6c206265636f6d652066726565206f6e20746865206e6578742074616c6c792e40536e617073686f7465645374616b657301003c5665633c543a3a42616c616e63653e040004e820546865207374616b6573206173207468657920776572652061742074686520706f696e7420746861742074686520766f746520656e6465642e2c4c6561646572626f61726400007c5665633c28543a3a42616c616e63652c20543a3a4163636f756e744964293e040004e02047657420746865206c6561646572626f6172642069662077653b726520696e207468652070726573656e746174696f6e2070686173652e38636f756e63696c5f766f74696e67184d6f64756c651043616c6c1400001c70726f706f7365042070726f706f73616c40426f783c543a3a50726f706f73616c3e00010010766f7465082070726f706f73616c1c543a3a486173681c617070726f766510626f6f6c000200107665746f043470726f706f73616c5f686173681c543a3a48617368000300487365745f636f6f6c6f66665f706572696f640418626c6f636b73903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a54797065000400447365745f766f74696e675f706572696f640418626c6f636b73903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a54797065000134436f756e63696c566f74696e672034436f6f6c6f6666506572696f64010038543a3a426c6f636b4e756d62657220e8030000000000000030566f74696e67506572696f64010038543a3a426c6f636b4e756d6265722003000000000000000040456e61637444656c6179506572696f64010038543a3a426c6f636b4e756d62657220000000000000000004cd01204e756d626572206f6620626c6f636b7320627920776869636820746f2064656c617920656e6163746d656e74206f66207375636365737366756c2c206e6f6e2d756e616e696d6f75732d636f756e63696c2d696e7374696761746564207265666572656e64756d2070726f706f73616c732e2450726f706f73616c730100785665633c28543a3a426c6f636b4e756d6265722c20543a3a48617368293e0400002850726f706f73616c4f6600011c543a3a486173682c543a3a50726f706f73616c0400003850726f706f73616c566f7465727301011c543a3a48617368445665633c543a3a4163636f756e7449643e04000034436f756e63696c566f74654f6600015c28543a3a486173682c20543a3a4163636f756e7449642910626f6f6c040000385665746f656450726f706f73616c00011c543a3a486173688c28543a3a426c6f636b4e756d6265722c205665633c543a3a4163636f756e7449643e290400003c636f756e63696c5f6d6f74696f6e73184d6f64756c651043616c6c0800001c70726f706f736508247468726573686f6c6430436f6d706163743c7533323e2070726f706f73616c6c426f783c3c542061732054726169743e3a3a50726f706f73616c3e00010010766f74650c2070726f706f73616c1c543a3a4861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c000138436f756e63696c4d6f74696f6e73102450726f706f73616c730100305665633c543a3a486173683e04000498205468652028686173686573206f662920746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600011c543a3a48617368583c542061732054726169743e3a3a50726f706f73616c040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700011c543a3a48617368e82850726f706f73616c496e6465782c207533322c205665633c543a3a4163636f756e7449643e2c205665633c543a3a4163636f756e7449643e29040004250120566f74657320666f72206120676976656e2070726f706f73616c3a202872657175697265645f7965735f766f7465732c207965735f766f746572732c206e6f5f766f74657273292e3450726f706f73616c436f756e7401000c753332100000000004482050726f706f73616c7320736f206661722e1c6772616e647061184d6f64756c651043616c6c040000487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e013c4772616e64706146696e616c697479043450656e64696e674368616e67650000c853746f72656450656e64696e674368616e67653c543a3a426c6f636b4e756d6265722c20543a3a53657373696f6e4b65793e040000207472656173757279184d6f64756c651043616c6c1400003470726f706f73655f7370656e64081476616c7565803c543a3a42616c616e636520617320486173436f6d706163743e3a3a547970652c62656e65666963696172798c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c2d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e01001c7365745f706f74041c6e65775f706f74803c543a3a42616c616e636520617320486173436f6d706163743e3a3a5479706504b420536574207468652062616c616e6365206f662066756e647320617661696c61626c6520746f207370656e642e020024636f6e666967757265103470726f706f73616c5f626f6e641c5065726d696c6c5470726f706f73616c5f626f6e645f6d696e696d756d803c543a3a42616c616e636520617320486173436f6d706163743e3a3a54797065307370656e645f706572696f64903c543a3a426c6f636b4e756d62657220617320486173436f6d706163743e3a3a54797065106275726e1c5065726d696c6c0470202852652d29636f6e6669677572652074686973206d6f64756c652e03003c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e04fc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e040040617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e085d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e01205472656173757279203050726f706f73616c426f6e6401001c5065726d696c6c10000000000851012050726f706f7274696f6e206f662066756e647320746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c61636520612070726f706f73616c2e20416e206163636570746564dc2070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f65736e27742e4c50726f706f73616c426f6e644d696e696d756d010028543a3a42616c616e63654000000000000000000000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64010038543a3a426c6f636b4e756d626572200100000000000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e01001c5065726d696c6c10000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e0c506f74010028543a3a42616c616e6365400000000000000000000000000000000004cc20546f74616c2066756e647320617661696c61626c6520746f2074686973206d6f64756c6520666f72207370656e64696e672e3450726f706f73616c436f756e7401003450726f706f73616c496e646578100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300013450726f706f73616c496e6465788850726f706f73616c3c543a3a4163636f756e7449642c20543a3a42616c616e63653e0400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e24417070726f76616c730100485665633c50726f706f73616c496e6465783e040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e20636f6e7472616374184d6f64756c651043616c6c0800001063616c6c1010646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c7565803c543a3a42616c616e636520617320486173436f6d706163743e3a3a54797065246761735f6c696d6974703c543a3a47617320617320486173436f6d706163743e3a3a5479706510646174611c5665633c75383e082901204d616b6520612063616c6c20746f206120737065636966696564206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e2901204d616b6520612063616c6c20746f206120737065636966696564206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e0100186372656174651024656e646f776d656e74803c543a3a42616c616e636520617320486173436f6d706163743e3a3a54797065246761735f6c696d6974703c543a3a47617320617320486173436f6d706163743e3a3a547970652463746f725f636f64651c5665633c75383e10646174611c5665633c75383e244d01204372656174652061206e657720636f6e74726163742c206f7074696f6e616c6c79207472616e73666572696e6720736f6d652062616c616e636520746f207468652063726561746564206163636f756e742e0084204372656174696f6e20697320657865637574656420617320666f6c6c6f77733a004101202d207468652064657374696e6174696f6e206164647265737320697320636f6d7075746564206261736564206f6e207468652073656e64657220616e642068617368206f662074686520636f64652eb8202d206163636f756e7420697320637265617465642061742074686520636f6d707574656420616464726573732e6d01202d20746865206063746f725f636f64656020697320657865637574656420696e2074686520636f6e74657874206f6620746865206e65776c792063726561746564206163636f756e742e204275666665722072657475726e65645d0120202061667465722074686520657865637574696f6e206973207361766564206173207468652060636f646560206f6620746865206163636f756e742e205468617420636f64652077696c6c20626520696e766f6b6564b420202075706f6e20616e79206d6573736167652072656365697665642062792074686973206163636f756e742e00107375646f184d6f64756c651043616c6c080000107375646f042070726f706f73616c40426f783c543a3a50726f706f73616c3e0001001c7365745f6b6579040c6e65778c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001105375646f040c4b6579010030543a3a4163636f756e744964800000000000000000000000000000000000000000000000000000000000000000001043616c6c382454696d657374616d702474696d657374616d70000024436f6e73656e73757324636f6e73656e73757301001c496e64696365731c696e646963657302002042616c616e6365732062616c616e63657303001c53657373696f6e1c73657373696f6e04001c5374616b696e671c7374616b696e6705002444656d6f63726163792464656d6f637261637906001c436f756e63696c1c636f756e63696c070034436f756e63696c566f74696e6738636f756e63696c5f766f74696e67080038436f756e63696c4d6f74696f6e733c636f756e63696c5f6d6f74696f6e7309001c4772616e6470611c6772616e6470610a002054726561737572792074726561737572790b0020436f6e747261637420636f6e74726163740c00105375646f107375646f0d00'; + +// Export the latest RPC response for metadata +export default meta; diff --git a/packages/types/src/Metadata/v1/Calls.ts b/packages/types/src/Metadata/v1/Calls.ts new file mode 100644 index 0000000000..b45e39359c --- /dev/null +++ b/packages/types/src/Metadata/v1/Calls.ts @@ -0,0 +1,77 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; + +export class MetadataCallArg extends Struct { + constructor (value?: any) { + super({ + name: Text, + type: Type + }, value); + } + + /** + * @description The argument name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The [[Type]] + */ + get type (): Type { + return this.get('type') as Type; + } +} + +/** + * @name MetadataCall + * @description + * The definition of a call + */ +export class MetadataCall extends Struct { + constructor (value?: any) { + super({ + // NOTE Just commented out, it is not available in current, but an open question + // around the usefulness of supplying this remains (could be re-added) + // id: u16, + name: Text, + args: Vector.with(MetadataCallArg), + docs: Vector.with(Text) + }, value); + } + + /** + * @description The [[MetadataCallArg]] for arguments + */ + get args (): Vector { + return this.get('args') as Vector; + } + + /** + * @description The [[Text]] documentation + */ + get docs (): Vector { + return this.get('docs') as Vector; + } + + // /** + // * @description The call function id + // */ + // get id (): u16 { + // return this.get('id') as u16; + // } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } +} diff --git a/packages/types/src/Metadata/v1/Events.ts b/packages/types/src/Metadata/v1/Events.ts new file mode 100644 index 0000000000..946252b21b --- /dev/null +++ b/packages/types/src/Metadata/v1/Events.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; + +/** + * @name MetadataEvent + * @description + * The definition of an event + */ +export class MetadataEvent extends Struct { + constructor (value?: any) { + super({ + name: Text, + args: Vector.with(Type), + docs: Vector.with(Text) + }, value); + } + + /** + * @description The [[Type]] for args + */ + get args (): Vector { + return this.get('args') as Vector; + } + + /** + * @description The [[Text]] documentation + */ + get docs (): Vector { + return this.get('docs') as Vector; + } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } +} diff --git a/packages/types/src/Metadata/v1/Metadata.spec.ts b/packages/types/src/Metadata/v1/Metadata.spec.ts new file mode 100644 index 0000000000..6dffa5c1c8 --- /dev/null +++ b/packages/types/src/Metadata/v1/Metadata.spec.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata'; + +import createType from '../../codec/createType'; +import Method from '../../primitive/Method'; + +import Metadata from '../Metadata'; +import latestParsed from './latest.substrate.v1.json'; +import rpcData from './static'; + +describe('MetadataV1', () => { + const metadata = new Metadata(rpcData); + + it('decodes latest properly', () => { + const str = JSON.stringify(metadata.toJSON()); + + expect(metadata.version).toBe(1); + expect(metadata.asV1.modules.length).not.toBe(0); + expect(str).toEqual(JSON.stringify(latestParsed)); + }); + + it('converts v1 to V0', () => { + const v0 = metadata.asV0; + + expect(metadata.getUniqTypes(true)).toEqual(v0.getUniqTypes(true)); + }); + + describe('storage with default values', () => { + Method.injectMethods(extrinsicsFromMeta(metadata.asV0)); + + metadata.asV1.modules + .filter(({ storage }) => storage.isSome) + .map((mod) => + mod.storage.unwrap().forEach(({ fallback, name, type }) => { + it(`creates default types for ${mod.prefix}.${name}, type ${type}`, () => { + expect( + () => createType(type.toString(), fallback) + ).not.toThrow(); + }); + }) + ); + }); +}); diff --git a/packages/types/src/Metadata/v1/Metadata.ts b/packages/types/src/Metadata/v1/Metadata.ts new file mode 100644 index 0000000000..a575e46def --- /dev/null +++ b/packages/types/src/Metadata/v1/Metadata.ts @@ -0,0 +1,130 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { MetadataInterface } from '../types'; + +import Option from '../../codec/Option'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import { flattenUniq, validateTypes } from '../util'; + +import { MetadataCall } from './Calls'; +import { MetadataEvent } from './Events'; +import { MetadataStorage } from './Storage'; + +/** + * @name MetadataModule + * @description + * The definition of a module in the system + */ +export class MetadataModule extends Struct { + constructor (value?: any) { + super({ + name: Text, + prefix: Text, + storage: Option.with(Vector.with(MetadataStorage)), + calls: Option.with(Vector.with(MetadataCall)), + events: Option.with(Vector.with(MetadataEvent)) + }, value); + } + + /** + * @description the module calls + */ + get calls (): Option> { + return this.get('calls') as Option>; + } + + /** + * @description the module events + */ + get events (): Option> { + return this.get('events') as Option>; + } + + /** + * @description the module name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description the module prefix + */ + get prefix (): Text { + return this.get('prefix') as Text; + } + + /** + * @description the associated module storage + */ + get storage (): Option> { + return this.get('storage') as Option>; + } +} + +/** + * @name MetadataV1 + * @description + * The runtime metadata as a decoded structure + */ +export default class MetadataV1 extends Struct implements MetadataInterface { + constructor (value?: any) { + super({ + modules: Vector.with(MetadataModule) + }, value); + } + + /** + * @description The associated modules for this structure + */ + get modules (): Vector { + return this.get('modules') as Vector; + } + + private get callNames () { + return this.modules.map((mod) => + mod.calls.isNone + ? [] + : mod.calls.unwrap().map((fn) => + fn.args.map((arg) => arg.type.toString()) + ) + ); + } + + private get eventNames () { + return this.modules.map((mod) => + mod.events.isNone + ? [] + : mod.events.unwrap().map((event) => + event.args.map((arg) => arg.toString()) + ) + ); + } + + private get storageNames () { + return this.modules.map((mod) => + mod.storage.isNone + ? [] + : mod.storage.unwrap().map((fn) => + fn.type.isMap + ? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()] + : [fn.type.asType.toString()] + ) + ); + } + + /** + * @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated + */ + getUniqTypes (throwError: boolean): Array { + const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]); + + validateTypes(types, throwError); + + return types; + } +} diff --git a/packages/types/src/Metadata/v1/Storage.ts b/packages/types/src/Metadata/v1/Storage.ts new file mode 100644 index 0000000000..99958b3891 --- /dev/null +++ b/packages/types/src/Metadata/v1/Storage.ts @@ -0,0 +1,165 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../../codec/EnumType'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Bytes from '../../primitive/Bytes'; +import Null from '../../primitive/Null'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; + +export class Default extends Null {} + +export class Optional extends Null {} + +export class MetadataStorageModifier extends EnumType { + constructor (value?: any, index?: number) { + super({ + Optional, + Default + }, value, index); + } + + /** + * @description `true` if the storage entry is optional + */ + get isOptional (): boolean { + return this.toNumber() === 0; + } + + toJSON (): any { + return this.toString(); + } +} + +export class MapType extends Struct { + private _isLinked = false; + + constructor (value?: any) { + super({ + key: Type, + value: Type + }, value); + + if (value && value.isLinked) { + this._isLinked = true; + } + } + + /** + * @description The mapped key as [[Type]] + */ + get key (): Type { + return this.get('key') as Type; + } + + /** + * @description The mapped value as [[Type]] + */ + get value (): Type { + return this.get('value') as Type; + } + + /** + * @description Is this an enumerable linked map + */ + get isLinked (): boolean { + return this._isLinked; + } +} + +export class PlainType extends Type { +} + +export class MetadataStorageType extends EnumType { + constructor (value?: any, index?: number) { + super({ + PlainType, + MapType + }, value, index); + } + + /** + * @description `true` if the storage entry is a map + */ + get isMap (): boolean { + return this.toNumber() === 1; + } + + /** + * @description The value as a mapped value + */ + get asMap (): MapType { + return this.value as MapType; + } + + /** + * @description The value as a [[Type]] value + */ + get asType (): PlainType { + return this.value as PlainType; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.isMap + ? this.asMap.value.toString() + : this.asType.toString(); + } +} + +/** + * @name MetadataModule + * @description + * The definition of a storage function + */ +export class MetadataStorage extends Struct { + constructor (value?: any) { + super({ + name: Text, + modifier: MetadataStorageModifier, + type: MetadataStorageType, + fallback: Bytes, + docs: Vector.with(Text) + }, value); + } + + /** + * @description The [[Text]] documentation + */ + get docs (): Vector { + return this.get('docs') as Vector; + } + + /** + * @description The [[Bytes]] fallback default + */ + get fallback (): Bytes { + return this.get('fallback') as Bytes; + } + + /** + * @description The [[MetadataArgument]] for arguments + */ + get modifier (): MetadataStorageModifier { + return this.get('modifier') as MetadataStorageModifier; + } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The [[MetadataStorageType]] + */ + get type (): MetadataStorageType { + return this.get('type') as MetadataStorageType; + } +} diff --git a/packages/types/src/Metadata/v1/index.ts b/packages/types/src/Metadata/v1/index.ts new file mode 100644 index 0000000000..baabf104e4 --- /dev/null +++ b/packages/types/src/Metadata/v1/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as MetadataV1, MetadataModule as MetadataModuleV1 } from './Metadata'; + +export default MetadataV1; + +export { + MetadataModuleV1 +}; diff --git a/packages/types/src/Metadata/v1/latest.substrate.v0.json b/packages/types/src/Metadata/v1/latest.substrate.v0.json new file mode 100644 index 0000000000..6ea63e420d --- /dev/null +++ b/packages/types/src/Metadata/v1/latest.substrate.v0.json @@ -0,0 +1,2958 @@ +{ + "outerEvent": { + "name": "Event", + "events": [ + [ + "system", + [ + { + "name": "ExtrinsicSuccess", + "arguments": [], + "documentation": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "arguments": [], + "documentation": [ + " An extrinsic failed." + ] + } + ] + ], + [ + "indices", + [ + { + "name": "NewAccountIndex", + "arguments": [ + "AccountId", + "AccountIndex" + ], + "documentation": [ + " A new account was created." + ] + } + ] + ], + [ + "balances", + [ + { + "name": "NewAccount", + "arguments": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "arguments": [ + "AccountId" + ], + "documentation": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "arguments": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "documentation": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + ], + [ + "session", + [ + { + "name": "NewSession", + "arguments": [ + "BlockNumber" + ], + "documentation": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + ], + [ + "staking", + [ + { + "name": "Reward", + "arguments": [ + "Balance" + ], + "documentation": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "arguments": [ + "AccountId", + "u32" + ], + "documentation": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "arguments": [ + "AccountId", + "Balance" + ], + "documentation": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + ], + [ + "democracy", + [ + { + "name": "Tabled", + "arguments": [ + "PropIndex", + "Balance", + "Vec" + ], + "documentation": [] + }, + { + "name": "Started", + "arguments": [ + "ReferendumIndex", + "VoteThreshold" + ], + "documentation": [] + }, + { + "name": "Passed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "NotPassed", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Cancelled", + "arguments": [ + "ReferendumIndex" + ], + "documentation": [] + }, + { + "name": "Executed", + "arguments": [ + "ReferendumIndex", + "bool" + ], + "documentation": [] + } + ] + ], + [ + "council", + [ + { + "name": "VoterReaped", + "arguments": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "arguments": [ + "AccountId" + ], + "documentation": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "arguments": [ + "u32" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalised", + "arguments": [ + "Vec", + "Vec" + ], + "documentation": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + ], + [ + "council_voting", + [ + { + "name": "TallyCancelation", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "arguments": [ + "Hash", + "u32", + "u32", + "u32" + ], + "documentation": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + ], + [ + "council_motions", + [ + { + "name": "Proposed", + "arguments": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "documentation": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "arguments": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "documentation": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "arguments": [ + "Hash" + ], + "documentation": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "arguments": [ + "Hash", + "bool" + ], + "documentation": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + ], + [ + "grandpa", + [ + { + "name": "NewAuthorities", + "arguments": [ + "Vec<(SessionKey,u64)>" + ], + "documentation": [ + " New authority set has been applied." + ] + } + ] + ], + [ + "treasury", + [ + { + "name": "Proposed", + "arguments": [ + "ProposalIndex" + ], + "documentation": [ + " New proposal." + ] + }, + { + "name": "Spending", + "arguments": [ + "Balance" + ], + "documentation": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "arguments": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "documentation": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "arguments": [ + "Balance" + ], + "documentation": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "arguments": [ + "Balance" + ], + "documentation": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + ], + [ + "contract", + [ + { + "name": "Transfer", + "arguments": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`." + ] + }, + { + "name": "Instantiated", + "arguments": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " Contract deployed by address at the specified address." + ] + }, + { + "name": "CodeStored", + "arguments": [ + "Hash" + ], + "documentation": [ + " Code with the specified hash has been stored." + ] + }, + { + "name": "ScheduleUpdated", + "arguments": [ + "u32" + ], + "documentation": [ + " Triggered when the current schedule is updated." + ] + }, + { + "name": "Dispatched", + "arguments": [ + "AccountId", + "bool" + ], + "documentation": [ + " A call was dispatched from the given account. The bool signals whether it was", + " successful execution or not." + ] + } + ] + ], + [ + "sudo", + [ + { + "name": "Sudid", + "arguments": [ + "bool" + ], + "documentation": [ + " A sudo just took place." + ] + }, + { + "name": "KeyChanged", + "arguments": [ + "AccountId" + ], + "documentation": [ + " The sudoer just switched identity; the old key is supplied." + ] + } + ] + ] + ] + }, + "modules": [ + { + "prefix": "system", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": { + "prefix": "System", + "functions": [ + { + "name": "AccountNonce", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Index" + } + }, + "default": "0x0000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "BlockHash", + "modifier": 0, + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Hash" + } + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicData", + "modifier": 0, + "type": { + "MapType": { + "key": "u32", + "value": "Bytes" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "RandomSeed", + "modifier": 0, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "Number", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": 0, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "ExtrinsicsRoot", + "modifier": 0, + "type": { + "PlainType": "Hash" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + }, + { + "name": "Digest", + "modifier": 0, + "type": { + "PlainType": "Digest" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "Events", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "aura", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": null + }, + { + "prefix": "timestamp", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set", + "arguments": [ + { + "name": "now", + "type": "Compact" + } + ], + "documentation": [ + " Set the current time.", + "", + " Extrinsic with this call should be placed at the specific position in the each block", + " (specified by the Trait::TIMESTAMP_SET_POSITION) typically at the start of the each block.", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `block_period`." + ] + } + ] + } + }, + "storage": { + "prefix": "Timestamp", + "functions": [ + { + "name": "Now", + "modifier": 0, + "type": { + "PlainType": "Moment" + }, + "default": "0x0000000000000000", + "documentation": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": 0, + "type": { + "PlainType": "Moment" + }, + "default": "0x0500000000000000", + "documentation": [ + " The minimum (and advised) period between blocks." + ] + }, + { + "name": "DidUpdate", + "modifier": 0, + "type": { + "PlainType": "bool" + }, + "default": "0x00", + "documentation": [ + " Did the timestamp get updated in this block?" + ] + } + ] + } + }, + { + "prefix": "consensus", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + }, + { + "id": 1, + "name": "note_offline", + "arguments": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "documentation": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "id": 2, + "name": "remark", + "arguments": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "documentation": [ + " Make some on-chain remark." + ] + }, + { + "id": 3, + "name": "set_heap_pages", + "arguments": [ + { + "name": "pages", + "type": "u64" + } + ], + "documentation": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "id": 4, + "name": "set_code", + "arguments": [ + { + "name": "new", + "type": "Bytes" + } + ], + "documentation": [ + " Set the new code." + ] + }, + { + "id": 5, + "name": "set_storage", + "arguments": [ + { + "name": "items", + "type": "Vec" + } + ], + "documentation": [ + " Set some items of storage." + ] + } + ] + } + }, + "storage": { + "prefix": "Consensus", + "functions": [ + { + "name": "OriginalAuthorities", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "indices", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [] + } + }, + "storage": { + "prefix": "Indices", + "functions": [ + { + "name": "NextEnumSet", + "modifier": 0, + "type": { + "PlainType": "AccountIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountIndex", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " The enumeration sets." + ] + } + ] + } + }, + { + "prefix": "balances", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "transfer", + "arguments": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Transfer some liquid free balance to another staker." + ] + }, + { + "id": 1, + "name": "set_balance", + "arguments": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "documentation": [ + " Set the balances of a given account." + ] + } + ] + } + }, + "storage": { + "prefix": "Balances", + "functions": [ + { + "name": "TotalIssuance", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The total amount of stake on the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The minimum amount allowed to keep an account open." + ] + }, + { + "name": "TransferFee", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee required to create an account. At least as big as ReclaimRebate." + ] + }, + { + "name": "FreeBalance", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It is", + " alone used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to cleanup data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable. (This is different", + " and wholly unrelated to the `Bondage` system used in the staking module.)", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + } + ] + } + }, + { + "prefix": "session", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_key", + "arguments": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "documentation": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "id": 1, + "name": "set_length", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "id": 2, + "name": "force_new_session", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Forces a new session." + ] + } + ] + } + }, + "storage": { + "prefix": "Session", + "functions": [ + { + "name": "Validators", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": 0, + "type": { + "PlainType": "Moment" + }, + "default": "0x0000000000000000", + "documentation": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": 0, + "type": { + "PlainType": "bool" + }, + "default": "0x00", + "documentation": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "SessionKey" + } + }, + "default": "0x00", + "documentation": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " The next session length." + ] + } + ] + } + }, + { + "prefix": "staking", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "stake", + "arguments": [], + "documentation": [ + " Declare the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 1, + "name": "unstake", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + } + ], + "documentation": [ + " Retract the desire to stake for the transactor.", + "", + " Effects will be felt at the beginning of the next era." + ] + }, + { + "id": 2, + "name": "nominate", + "arguments": [ + { + "name": "target", + "type": "Address" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "unnominate", + "arguments": [ + { + "name": "target_index", + "type": "Compact" + } + ], + "documentation": [ + " Will panic if called when source isn't currently nominating target.", + " Updates Nominating, NominatorsFor and NominationBalance." + ] + }, + { + "id": 4, + "name": "register_preferences", + "arguments": [ + { + "name": "intentions_index", + "type": "Compact" + }, + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "documentation": [ + " Set the given account's preference for slashing behaviour should they be a validator.", + "", + " An error (no-op) if `Self::intentions()[intentions_index] != origin`." + ] + }, + { + "id": 5, + "name": "set_sessions_per_era", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the number of sessions in an era." + ] + }, + { + "id": 6, + "name": "set_bonding_duration", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The length of the bonding duration in eras." + ] + }, + { + "id": 7, + "name": "set_validator_count", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " The ideal number of validators." + ] + }, + { + "id": 8, + "name": "force_new_era", + "arguments": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "documentation": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "id": 9, + "name": "set_offline_slash_grace", + "arguments": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Set the offline slash grace period." + ] + }, + { + "id": 10, + "name": "set_invulnerables", + "arguments": [ + { + "name": "validators", + "type": "Vec" + } + ], + "documentation": [ + " Set the validators who cannot be slashed (if any)." + ] + } + ] + } + }, + "storage": { + "prefix": "Staking", + "functions": [ + { + "name": "ValidatorCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x04000000", + "documentation": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": 0, + "type": { + "PlainType": "Perbill" + }, + "default": "0x3c000000", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": 0, + "type": { + "PlainType": "Perbill" + }, + "default": "0x40420f00", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "Invulerables", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " Any validators that may never be slashed or forcible kicked. It's a Vec since they're easy to initialise", + " and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets." + ] + }, + { + "name": "CurrentEra", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The current era index." + ] + }, + { + "name": "ValidatorPreferences", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "ValidatorPrefs" + } + }, + "default": "0x0c00", + "documentation": [ + " Preferences that a validator has." + ] + }, + { + "name": "Intentions", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " All the accounts with a desire to stake." + ] + }, + { + "name": "Nominating", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountId" + } + }, + "default": "0x00", + "documentation": [ + " All nominator -> nominee relationships." + ] + }, + { + "name": "NominatorsFor", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Nominators for a particular account." + ] + }, + { + "name": "CurrentNominatorsFor", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Nominators for a particular account that is in action right now." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentOfflineSlash", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x00", + "documentation": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "StakeRange", + "modifier": 0, + "type": { + "PlainType": "(Balance,Balance)" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " The highest and lowest staked validator slashable balances." + ] + }, + { + "name": "Bondage", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "BlockNumber" + } + }, + "default": "0x0000000000000000", + "documentation": [ + " The block at which the `who`'s funds become entirely liquid." + ] + }, + { + "name": "SlashCount", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "u32" + } + }, + "default": "0x00000000", + "documentation": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": 0, + "type": { + "PlainType": "Null" + }, + "default": "0x00", + "documentation": [ + " We are forcing a new era." + ] + } + ] + } + }, + { + "prefix": "democracy", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 1, + "name": "second", + "arguments": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken." + ] + }, + { + "id": 2, + "name": "vote", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "Vote" + } + ], + "documentation": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo." + ] + }, + { + "id": 3, + "name": "start_referendum", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Start a referendum." + ] + }, + { + "id": 4, + "name": "cancel_referendum", + "arguments": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a referendum." + ] + }, + { + "id": 5, + "name": "cancel_queued", + "arguments": [ + { + "name": "when", + "type": "Compact" + }, + { + "name": "which", + "type": "Compact" + } + ], + "documentation": [ + " Cancel a proposal queued for enactment." + ] + } + ] + } + }, + "storage": { + "prefix": "Democracy", + "functions": [ + { + "name": "PublicPropCount", + "modifier": 0, + "type": { + "PlainType": "PropIndex" + }, + "default": "0x00000000", + "documentation": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": 0, + "type": { + "PlainType": "Vec<(PropIndex,Proposal,AccountId)>" + }, + "default": "0x00", + "documentation": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": 0, + "type": { + "MapType": { + "key": "PropIndex", + "value": "(Balance,Vec)" + } + }, + "default": "0x00", + "documentation": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "MaxLockPeriods", + "modifier": 0, + "type": { + "PlainType": "LockPeriods" + }, + "default": "0x00", + "documentation": [ + " The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`." + ] + }, + { + "name": "VotingPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": 0, + "type": { + "PlainType": "ReferendumIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": 0, + "type": { + "PlainType": "ReferendumIndex" + }, + "default": "0x00000000", + "documentation": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": 0, + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "ReferendumInfo" + } + }, + "default": "0x00", + "documentation": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": 0, + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Vec>" + } + }, + "default": "0x00", + "documentation": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "Bondage", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "BlockNumber" + } + }, + "default": "0x0000000000000000", + "documentation": [ + " The block at which the `who`'s funds become liquid." + ] + }, + { + "name": "VotersFor", + "modifier": 0, + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": 0, + "type": { + "MapType": { + "key": "(ReferendumIndex,AccountId)", + "value": "Vote" + } + }, + "default": "0x00", + "documentation": [ + " Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the", + " voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check", + " `voters_for`, then you can also check for simple existence with `VoteOf::exists` first." + ] + } + ] + } + }, + { + "prefix": "council", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "set_approvals", + "arguments": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "id": 1, + "name": "reap_inactive_voter", + "arguments": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "id": 2, + "name": "retract_voter", + "arguments": [ + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "id": 3, + "name": "submit_candidacy", + "arguments": [ + { + "name": "slot", + "type": "Compact" + } + ], + "documentation": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "id": 4, + "name": "present_winner", + "arguments": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "documentation": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "id": 5, + "name": "set_desired_seats", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "id": 6, + "name": "remove_member", + "arguments": [ + { + "name": "who", + "type": "Address" + } + ], + "documentation": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "id": 7, + "name": "set_presentation_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalise_vote`." + ] + }, + { + "id": 8, + "name": "set_term_duration", + "arguments": [ + { + "name": "count", + "type": "Compact" + } + ], + "documentation": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalise_vote`." + ] + } + ] + } + }, + "storage": { + "prefix": "Council", + "functions": [ + { + "name": "CandidacyBond", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x09000000000000000000000000000000", + "documentation": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x01000000000000000000000000000000", + "documentation": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x02000000", + "documentation": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": 0, + "type": { + "PlainType": "VoteIndex" + }, + "default": "0x01000000", + "documentation": [ + " How many vote indexes need to go by after a target voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0500000000000000", + "documentation": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": 0, + "type": { + "PlainType": "Vec<(AccountId,BlockNumber)>" + }, + "default": "0x00", + "documentation": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters. The block number (second element in the tuple) is the block that their position is", + " active until (calculated by the sum of the block number when the council member was elected", + " and their term duration)." + ] + }, + { + "name": "VoteCount", + "modifier": 0, + "type": { + "PlainType": "VoteIndex" + }, + "default": "0x00000000", + "documentation": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [ + " A list of votes for each voter, respecting the last cleared vote index that this voter was", + " last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "(VoteIndex,u32)" + } + }, + "default": "0x00", + "documentation": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": 0, + "type": { + "MapType": { + "key": "AccountId", + "value": "VoteIndex" + } + }, + "default": "0x00", + "documentation": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [] + }, + { + "name": "NextFinalise", + "modifier": 0, + "type": { + "PlainType": "(BlockNumber,u32,Vec)" + }, + "default": "0x00", + "documentation": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": 0, + "type": { + "PlainType": "Vec<(Balance,AccountId)>" + }, + "default": "0x00", + "documentation": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ] + } + }, + { + "prefix": "council_voting", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + }, + { + "id": 2, + "name": "veto", + "arguments": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [] + }, + { + "id": 3, + "name": "set_cooloff_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + }, + { + "id": 4, + "name": "set_voting_period", + "arguments": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilVoting", + "functions": [ + { + "name": "CooloffPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0xe803000000000000", + "documentation": [] + }, + { + "name": "VotingPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0300000000000000", + "documentation": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0000000000000000", + "documentation": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": 0, + "type": { + "PlainType": "Vec<(BlockNumber,Hash)>" + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "ProposalVoters", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "Vec" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "CouncilVoteOf", + "modifier": 0, + "type": { + "MapType": { + "key": "(Hash,AccountId)", + "value": "bool" + } + }, + "default": "0x00", + "documentation": [] + }, + { + "name": "VetoedProposal", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "(BlockNumber,Vec)" + } + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "council_motions", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose", + "arguments": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "vote", + "arguments": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "CouncilMotions", + "functions": [ + { + "name": "Proposals", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": 0, + "type": { + "MapType": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)" + } + }, + "default": "0x00", + "documentation": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": 0, + "type": { + "PlainType": "u32" + }, + "default": "0x00000000", + "documentation": [ + " Proposals so far." + ] + } + ] + } + }, + { + "prefix": "grandpa", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "report_misbehavior", + "arguments": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "documentation": [ + " Report some misbehaviour." + ] + } + ] + } + }, + "storage": { + "prefix": "GrandpaFinality", + "functions": [ + { + "name": "PendingChange", + "modifier": 0, + "type": { + "PlainType": "StoredPendingChange" + }, + "default": "0x00", + "documentation": [] + } + ] + } + }, + { + "prefix": "treasury", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "propose_spend", + "arguments": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "documentation": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "id": 1, + "name": "set_pot", + "arguments": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "documentation": [ + " Set the balance of funds available to spend." + ] + }, + { + "id": 2, + "name": "configure", + "arguments": [ + { + "name": "proposal_bond", + "type": "Compact" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Compact" + } + ], + "documentation": [ + " (Re-)configure this module." + ] + }, + { + "id": 3, + "name": "reject_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "id": 4, + "name": "approve_proposal", + "arguments": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ] + } + }, + "storage": { + "prefix": "Treasury", + "functions": [ + { + "name": "ProposalBond", + "modifier": 0, + "type": { + "PlainType": "Permill" + }, + "default": "0x00000000", + "documentation": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": 0, + "type": { + "PlainType": "BlockNumber" + }, + "default": "0x0100000000000000", + "documentation": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": 0, + "type": { + "PlainType": "Permill" + }, + "default": "0x00000000", + "documentation": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": 0, + "type": { + "PlainType": "Balance" + }, + "default": "0x00000000000000000000000000000000", + "documentation": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": 0, + "type": { + "PlainType": "ProposalIndex" + }, + "default": "0x00000000", + "documentation": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": 0, + "type": { + "MapType": { + "key": "ProposalIndex", + "value": "Proposal" + } + }, + "default": "0x00", + "documentation": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": 0, + "type": { + "PlainType": "Vec" + }, + "default": "0x00", + "documentation": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + } + }, + { + "prefix": "contract", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "update_schedule", + "arguments": [ + { + "name": "schedule", + "type": "Schedule" + } + ], + "documentation": [ + " Updates the schedule for metering contracts.", + "", + " The schedule must have a greater version than the stored schedule." + ] + }, + { + "id": 1, + "name": "put_code", + "arguments": [ + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code", + "type": "Bytes" + } + ], + "documentation": [ + " Stores code in the storage. You can instantiate contracts only with stored code." + ] + }, + { + "id": 2, + "name": "call", + "arguments": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "documentation": [ + " Make a call to a specified account, optionally transferring some balance." + ] + }, + { + "id": 3, + "name": "create", + "arguments": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code_hash", + "type": "CodeHash" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "documentation": [ + " Create a new contract, optionally transfering some balance to the created account.", + "", + " Creation is executed as follows:", + "", + " - the destination address is computed based on the sender and hash of the code.", + " - account is created at the computed address.", + " - the `ctor_code` is executed in the context of the newly created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any message received by this account." + ] + } + ] + } + }, + "storage": null + }, + { + "prefix": "sudo", + "module": { + "name": "Module", + "call": { + "name": "Call", + "functions": [ + { + "id": 0, + "name": "sudo", + "arguments": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "documentation": [] + }, + { + "id": 1, + "name": "set_key", + "arguments": [ + { + "name": "new", + "type": "Address" + } + ], + "documentation": [] + } + ] + } + }, + "storage": { + "prefix": "Sudo", + "functions": [ + { + "name": "Key", + "modifier": 0, + "type": { + "PlainType": "AccountId" + }, + "default": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + } + ] + } + } + ], + "outerDispatch": { + "name": "Call", + "calls": [ + { + "name": "Timestamp", + "prefix": "timestamp", + "index": 0 + }, + { + "name": "Consensus", + "prefix": "consensus", + "index": 1 + }, + { + "name": "Indices", + "prefix": "indices", + "index": 2 + }, + { + "name": "Balances", + "prefix": "balances", + "index": 3 + }, + { + "name": "Session", + "prefix": "session", + "index": 4 + }, + { + "name": "Staking", + "prefix": "staking", + "index": 5 + }, + { + "name": "Democracy", + "prefix": "democracy", + "index": 6 + }, + { + "name": "Council", + "prefix": "council", + "index": 7 + }, + { + "name": "CouncilVoting", + "prefix": "council_voting", + "index": 8 + }, + { + "name": "CouncilMotions", + "prefix": "council_motions", + "index": 9 + }, + { + "name": "GrandpaFinality", + "prefix": "grandpa", + "index": 10 + }, + { + "name": "Treasury", + "prefix": "treasury", + "index": 11 + }, + { + "name": "", + "prefix": "contract", + "index": 12 + }, + { + "name": "Sudo", + "prefix": "sudo", + "index": 13 + } + ] + } +} diff --git a/packages/types/src/Metadata/v1/latest.substrate.v1.json b/packages/types/src/Metadata/v1/latest.substrate.v1.json new file mode 100644 index 0000000000..c1224b1b4c --- /dev/null +++ b/packages/types/src/Metadata/v1/latest.substrate.v1.json @@ -0,0 +1,3009 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "MetadataV1": { + "modules": [ + { + "name": "system", + "prefix": "System", + "storage": [ + { + "name": "AccountNonce", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Index" + } + }, + "fallback": "0x0000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Hash" + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "MapType": { + "key": "u32", + "value": "Bytes" + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "RandomSeed", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicsRoot", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "PlainType": "Digest" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": null, + "events": [ + { + "name": "ExtrinsicSuccess", + "args": [], + "docs": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "args": [], + "docs": [ + " An extrinsic failed." + ] + } + ] + }, + { + "name": "aura", + "prefix": "", + "storage": null, + "calls": null, + "events": null + }, + { + "name": "timestamp", + "prefix": "Timestamp", + "storage": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0500000000000000", + "docs": [ + " The minimum (and advised) period between blocks." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " Did the timestamp get updated in this block?" + ] + } + ], + "calls": [ + { + "name": "set", + "args": [ + { + "name": "now", + "type": "Compact" + } + ], + "docs": [ + " Set the current time.", + "", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `block_period`.", + " ", + " The dispatch origin for this call must be `Inherent`." + ] + } + ], + "events": null + }, + { + "name": "consensus", + "prefix": "Consensus", + "storage": [ + { + "name": "OriginalAuthorities", + "modifier": "Optional", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehaviour." + ] + }, + { + "name": "note_offline", + "args": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "docs": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "name": "remark", + "args": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "docs": [ + " Make some on-chain remark." + ] + }, + { + "name": "set_heap_pages", + "args": [ + { + "name": "pages", + "type": "u64" + } + ], + "docs": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "name": "set_code", + "args": [ + { + "name": "new", + "type": "Bytes" + } + ], + "docs": [ + " Set the new code." + ] + }, + { + "name": "set_storage", + "args": [ + { + "name": "items", + "type": "Vec" + } + ], + "docs": [ + " Set some items of storage." + ] + } + ], + "events": null + }, + { + "name": "indices", + "prefix": "Indices", + "storage": [ + { + "name": "NextEnumSet", + "modifier": "Default", + "type": { + "PlainType": "AccountIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountIndex", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [ + " The enumeration sets." + ] + } + ], + "calls": [], + "events": [ + { + "name": "NewAccountIndex", + "args": [ + "AccountId", + "AccountIndex" + ], + "docs": [ + " A new account index was assigned.", + "", + " This event is not triggered when an existing index is reassigned", + " to another `AccountId`." + ] + } + ] + }, + { + "name": "balances", + "prefix": "Balances", + "storage": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total amount of stake on the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount allowed to keep an account open." + ] + }, + { + "name": "TransferFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to create an account. At least as big as ReclaimRebate." + ] + }, + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VestingSchedule" + } + }, + "fallback": "0x00", + "docs": [ + " Information regarding the vesting of a given account." + ] + }, + { + "name": "FreeBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It is", + " alone used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to cleanup data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance" + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable. (This is different", + " and wholly unrelated to the `Bondage` system used in the staking module.)", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [ + " Any liquidity locks on some account balances." + ] + } + ], + "calls": [ + { + "name": "transfer", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Transfer some liquid free balance to another staker." + ] + }, + { + "name": "set_balance", + "args": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "docs": [ + " Set the balances of a given account." + ] + } + ], + "events": [ + { + "name": "NewAccount", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "args": [ + "AccountId" + ], + "docs": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "docs": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + }, + { + "name": "session", + "prefix": "Session", + "storage": [ + { + "name": "Validators", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": "Optional", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "SessionKey" + } + }, + "fallback": "0x00", + "docs": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next session length." + ] + } + ], + "calls": [ + { + "name": "set_key", + "args": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "docs": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "name": "set_length", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "name": "force_new_session", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Forces a new session." + ] + } + ], + "events": [ + { + "name": "NewSession", + "args": [ + "BlockNumber" + ], + "docs": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + }, + { + "name": "staking", + "prefix": "Staking", + "storage": [ + { + "name": "ValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x04000000", + "docs": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x3c000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x40420f00", + "docs": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "Invulerables", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "Invulnerables", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Any validators that may never be slashed or forcibly kicked. It's a Vec since they're easy to initialise", + " and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets." + ] + }, + { + "name": "Bonded", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountId" + } + }, + "fallback": "0x00", + "docs": [ + " Map from all locked \"stash\" accounts to the controller account." + ] + }, + { + "name": "Ledger", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "StakingLedger" + } + }, + "fallback": "0x00", + "docs": [ + " Map from all (unlocked) \"controller\" accounts to the info regarding the staking." + ] + }, + { + "name": "Payee", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "RewardDestination" + } + }, + "fallback": "0x00", + "docs": [ + " Where the reward payment should be made." + ] + }, + { + "name": "Validators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "ValidatorPrefs" + } + }, + "fallback": "0x0c00", + "docs": [ + " The set of keys are all controllers that want to validate.", + "", + " The values are the preferences that a validator has." + ] + }, + { + "name": "Nominators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [ + " The set of keys are all controllers that want to nominate.", + "", + " The value are the nominations." + ] + }, + { + "name": "Stakers", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Exposure" + } + }, + "fallback": "0x000000", + "docs": [ + " Nominators for a particular account that is in action right now. You can't iterate through validators here,", + " but you can find them in the `sessions` module." + ] + }, + { + "name": "CurrentEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current era index." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentOfflineSlash", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "CurrentEraReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The accumulated reward for the current era. Reset to zero at the beginning of the era and", + " increased for every successfully finished session." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "SlotStake", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of balance actively at stake for each validator slot, currently.", + "", + " This is used to derive rewards and punishments." + ] + }, + { + "name": "SlashCount", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "u32" + } + }, + "fallback": "0x00000000", + "docs": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": "Optional", + "type": { + "PlainType": "Null" + }, + "fallback": "0x00", + "docs": [ + " We are forcing a new era." + ] + }, + { + "name": "RecentlyOffline", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber,u32)>" + }, + "fallback": "0x00", + "docs": [ + " Most recent `RECENT_OFFLINE_COUNT` instances. (who it was, when it was reported, how many instances they were offline for)." + ] + } + ], + "calls": [ + { + "name": "bond", + "args": [ + { + "name": "controller", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " Take the origin account as a stash and lock up `value` of its balance. `controller` will be the", + " account that controls it." + ] + }, + { + "name": "bond_extra", + "args": [ + { + "name": "max_additional", + "type": "BalanceOf" + } + ], + "docs": [ + " Add some extra amount that have appeared in the stash `free_balance` into the balance up for", + " staking.", + "", + " Use this if there are additional funds in your stash account that you wish to bond.", + "", + " NOTE: This call must be made by the controller, not the stash." + ] + }, + { + "name": "unbond", + "args": [ + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Schedule a portion of the stash to be unlocked ready for transfer out after the bond", + " period ends. If this leaves an amount actively bonded less than", + " T::Currency::existential_deposit(), then it is increased to the full amount.", + "", + " Once the unlock period is done, you can call `withdraw_unbonded` to actually move", + " the funds out of management ready for transfer.", + "", + " NOTE: This call must be made by the controller, not the stash.", + "", + " See also [`Call::withdraw_unbonded`]." + ] + }, + { + "name": "withdraw_unbonded", + "args": [], + "docs": [ + " Remove any unlocked chunks from the `unlocking` queue from our management.", + "", + " This essentially frees up that balance to be used by the stash account to do", + " whatever it wants.", + "", + " NOTE: This call must be made by the controller, not the stash.", + "", + " See also [`Call::unbond`]." + ] + }, + { + "name": "validate", + "args": [ + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "docs": [ + " Declare the desire to validate for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " NOTE: This call must be made by the controller, not the stash." + ] + }, + { + "name": "nominate", + "args": [ + { + "name": "targets", + "type": "Vec
" + } + ], + "docs": [ + " Declare the desire to nominate `targets` for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " NOTE: This call must be made by the controller, not the stash." + ] + }, + { + "name": "chill", + "args": [], + "docs": [ + " Declare no desire to either validate or nominate.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " NOTE: This call must be made by the controller, not the stash." + ] + }, + { + "name": "set_payee", + "args": [ + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " NOTE: This call must be made by the controller, not the stash." + ] + }, + { + "name": "set_sessions_per_era", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the number of sessions in an era." + ] + }, + { + "name": "set_bonding_duration", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The length of the bonding duration in eras." + ] + }, + { + "name": "set_validator_count", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The ideal number of validators." + ] + }, + { + "name": "force_new_era", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "name": "set_offline_slash_grace", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the offline slash grace period." + ] + }, + { + "name": "set_invulnerables", + "args": [ + { + "name": "validators", + "type": "Vec" + } + ], + "docs": [ + " Set the validators who cannot be slashed (if any)." + ] + } + ], + "events": [ + { + "name": "Reward", + "args": [ + "Balance" + ], + "docs": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "args": [ + "AccountId", + "u32" + ], + "docs": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + }, + { + "name": "democracy", + "prefix": "Democracy", + "storage": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "PlainType": "PropIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "PlainType": "Vec<(PropIndex,Proposal,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "PropIndex", + "value": "(BalanceOf,Vec)" + } + }, + "fallback": "0x00", + "docs": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "MaxLockPeriods", + "modifier": "Default", + "type": { + "PlainType": "LockPeriods" + }, + "fallback": "0x00", + "docs": [ + " The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "ReferendumInfo" + } + }, + "fallback": "0x00", + "docs": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Vec>" + } + }, + "fallback": "0x00", + "docs": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "VotersFor", + "modifier": "Default", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "(ReferendumIndex,AccountId)", + "value": "Vote" + } + }, + "fallback": "0x00", + "docs": [ + " Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the", + " voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check", + " `voters_for`, then you can also check for simple existence with `VoteOf::exists` first." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "second", + "args": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "vote", + "args": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "Vote" + } + ], + "docs": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo." + ] + }, + { + "name": "start_referendum", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Start a referendum." + ] + }, + { + "name": "cancel_referendum", + "args": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a referendum." + ] + }, + { + "name": "cancel_queued", + "args": [ + { + "name": "when", + "type": "Compact" + }, + { + "name": "which", + "type": "Compact" + } + ], + "docs": [ + " Cancel a proposal queued for enactment." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "PropIndex", + "Balance" + ], + "docs": [] + }, + { + "name": "Tabled", + "args": [ + "PropIndex", + "Balance", + "Vec" + ], + "docs": [] + }, + { + "name": "Started", + "args": [ + "ReferendumIndex", + "VoteThreshold" + ], + "docs": [] + }, + { + "name": "Passed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "NotPassed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Cancelled", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Executed", + "args": [ + "ReferendumIndex", + "bool" + ], + "docs": [] + } + ] + }, + { + "name": "council", + "prefix": "Council", + "storage": [ + { + "name": "CandidacyBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x09000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x02000000", + "docs": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x01000000", + "docs": [ + " How many vote indexes need to go by after a target voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0500000000000000", + "docs": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber)>" + }, + "fallback": "0x00", + "docs": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters. The block number (second element in the tuple) is the block that their position is", + " active until (calculated by the sum of the block number when the council member was elected", + " and their term duration)." + ] + }, + { + "name": "VoteCount", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [ + " A list of votes for each voter, respecting the last cleared vote index that this voter was", + " last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "(VoteIndex,u32)" + } + }, + "fallback": "0x00", + "docs": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VoteIndex" + } + }, + "fallback": "0x00", + "docs": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [] + }, + { + "name": "NextFinalise", + "modifier": "Optional", + "type": { + "PlainType": "(BlockNumber,u32,Vec)" + }, + "fallback": "0x00", + "docs": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": "Optional", + "type": { + "PlainType": "Vec<(BalanceOf,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ], + "calls": [ + { + "name": "set_approvals", + "args": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "name": "reap_inactive_voter", + "args": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "name": "retract_voter", + "args": [ + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "name": "submit_candidacy", + "args": [ + { + "name": "slot", + "type": "Compact" + } + ], + "docs": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "name": "present_winner", + "args": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "name": "set_desired_seats", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "name": "remove_member", + "args": [ + { + "name": "who", + "type": "Address" + } + ], + "docs": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "name": "set_presentation_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalise_vote`." + ] + }, + { + "name": "set_term_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalise_vote`." + ] + } + ], + "events": [ + { + "name": "VoterReaped", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "args": [ + "AccountId" + ], + "docs": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "args": [ + "u32" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalised", + "args": [ + "Vec", + "Vec" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + }, + { + "name": "council_voting", + "prefix": "CouncilVoting", + "storage": [ + { + "name": "CooloffPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0300000000000000", + "docs": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec<(BlockNumber,Hash)>" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalVoters", + "modifier": "Default", + "type": { + "MapType": { + "key": "Hash", + "value": "Vec" + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "CouncilVoteOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "(Hash,AccountId)", + "value": "bool" + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "VetoedProposal", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(BlockNumber,Vec)" + } + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + }, + { + "name": "veto", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [] + }, + { + "name": "set_cooloff_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + }, + { + "name": "set_voting_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "TallyCancelation", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + }, + { + "name": "council_motions", + "prefix": "CouncilMotions", + "storage": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal" + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)" + } + }, + "fallback": "0x00", + "docs": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "docs": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "docs": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "bool" + ], + "docs": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + }, + { + "name": "finality_tracker", + "prefix": "", + "storage": null, + "calls": [ + { + "name": "final_hint", + "args": [ + { + "name": "hint", + "type": "Compact" + } + ], + "docs": [ + " Hint that the author of this block thinks the best finalized", + " block is the given number." + ] + } + ], + "events": null + }, + { + "name": "grandpa", + "prefix": "GrandpaFinality", + "storage": [ + { + "name": "PendingChange", + "modifier": "Optional", + "type": { + "PlainType": "StoredPendingChange" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "NextForced", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehaviour." + ] + } + ], + "events": [ + { + "name": "NewAuthorities", + "args": [ + "Vec<(SessionKey,u64)>" + ], + "docs": [ + " New authority set has been applied." + ] + } + ] + }, + { + "name": "treasury", + "prefix": "Treasury", + "storage": [ + { + "name": "ProposalBond", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0100000000000000", + "docs": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "ProposalIndex" + }, + "fallback": "0x00000000", + "docs": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ProposalIndex", + "value": "Proposal" + } + }, + "fallback": "0x00", + "docs": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ], + "calls": [ + { + "name": "propose_spend", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "docs": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "name": "set_pot", + "args": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "docs": [ + " Set the balance of funds available to spend." + ] + }, + { + "name": "configure", + "args": [ + { + "name": "proposal_bond", + "type": "Compact" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Compact" + } + ], + "docs": [ + " (Re-)configure this module." + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "name": "approve_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "ProposalIndex" + ], + "docs": [ + " New proposal." + ] + }, + { + "name": "Spending", + "args": [ + "Balance" + ], + "docs": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "args": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "docs": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "args": [ + "Balance" + ], + "docs": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "args": [ + "Balance" + ], + "docs": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + }, + { + "name": "contract", + "prefix": "Contract", + "storage": [ + { + "name": "ContractFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x15000000000000000000000000000000", + "docs": [ + " The fee required to create a contract. At least as big as staking's ReclaimRebate." + ] + }, + { + "name": "CallBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x8700000000000000", + "docs": [ + " The fee charged for a call into a contract." + ] + }, + { + "name": "CreateBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0xaf00000000000000", + "docs": [ + " The fee charged for a create of a contract." + ] + }, + { + "name": "GasPrice", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The price of one unit of gas." + ] + }, + { + "name": "MaxDepth", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x64000000", + "docs": [ + " The maximum nesting level of a call/create stack." + ] + }, + { + "name": "BlockGasLimit", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x40420f0000000000", + "docs": [ + " The maximum amount of gas that could be expended per block." + ] + }, + { + "name": "GasSpent", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Gas spent so far in this block." + ] + }, + { + "name": "CurrentSchedule", + "modifier": "Default", + "type": { + "PlainType": "Schedule" + }, + "fallback": "0x000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000000010010000000", + "docs": [ + " Current cost schedule for contracts." + ] + }, + { + "name": "CodeHashOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "CodeHash" + } + }, + "fallback": "0x00", + "docs": [ + " The code associated with a given account." + ] + }, + { + "name": "PristineCode", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "Bytes" + } + }, + "fallback": "0x00", + "docs": [ + " A mapping from an original code hash to the original code, untouched by instrumentation." + ] + }, + { + "name": "CodeStorage", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "PrefabWasmModule" + } + }, + "fallback": "0x00", + "docs": [ + " A mapping between an original code hash and instrumented wasm code, ready for the execution." + ] + } + ], + "calls": [ + { + "name": "update_schedule", + "args": [ + { + "name": "schedule", + "type": "Schedule" + } + ], + "docs": [ + " Updates the schedule for metering contracts.", + "", + " The schedule must have a greater version than the stored schedule." + ] + }, + { + "name": "put_code", + "args": [ + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [ + " Stores code in the storage. You can instantiate contracts only with stored code." + ] + }, + { + "name": "call", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Make a call to a specified account, optionally transferring some balance." + ] + }, + { + "name": "create", + "args": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code_hash", + "type": "CodeHash" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Create a new contract, optionally transfering some balance to the created account.", + "", + " Creation is executed as follows:", + "", + " - the destination address is computed based on the sender and hash of the code.", + " - account is created at the computed address.", + " - the `ctor_code` is executed in the context of the newly created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any message received by this account." + ] + } + ], + "events": [ + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`." + ] + }, + { + "name": "Instantiated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " Contract deployed by address at the specified address." + ] + }, + { + "name": "CodeStored", + "args": [ + "Hash" + ], + "docs": [ + " Code with the specified hash has been stored." + ] + }, + { + "name": "ScheduleUpdated", + "args": [ + "u32" + ], + "docs": [ + " Triggered when the current schedule is updated." + ] + }, + { + "name": "Dispatched", + "args": [ + "AccountId", + "bool" + ], + "docs": [ + " A call was dispatched from the given account. The bool signals whether it was", + " successful execution or not." + ] + } + ] + }, + { + "name": "sudo", + "prefix": "Sudo", + "storage": [ + { + "name": "Key", + "modifier": "Default", + "type": { + "PlainType": "AccountId" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + } + ], + "calls": [ + { + "name": "sudo", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "set_key", + "args": [ + { + "name": "new", + "type": "Address" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Sudid", + "args": [ + "bool" + ], + "docs": [ + " A sudo just took place." + ] + }, + { + "name": "KeyChanged", + "args": [ + "AccountId" + ], + "docs": [ + " The sudoer just switched identity; the old key is supplied." + ] + } + ] + }, + { + "name": "fees", + "prefix": "Fees", + "storage": [ + { + "name": "TransactionBaseFee", + "modifier": "Default", + "type": { + "PlainType": "AssetOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": "Default", + "type": { + "PlainType": "AssetOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "CurrentTransactionFee", + "modifier": "Default", + "type": { + "MapType": { + "key": "u32", + "value": "AssetOf" + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The `extrinsic_index => accumulated_fees` map, containing records to", + " track the overall charged fees for each transaction.", + "", + " All records should be removed at finalise stage." + ] + } + ], + "calls": null, + "events": [ + { + "name": "Charged", + "args": [ + "u32", + "Amount" + ], + "docs": [ + " Fee charged (extrinsic_index, fee_amount)" + ] + } + ] + } + ] + } + } +} diff --git a/packages/types/src/Metadata/v1/static.ts b/packages/types/src/Metadata/v1/static.ts new file mode 100644 index 0000000000..5e512471b2 --- /dev/null +++ b/packages/types/src/Metadata/v1/static.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const meta = '0x6d65746101481873797374656d1853797374656d012c304163636f756e744e6f6e6365010130543a3a4163636f756e74496420543a3a496e646578200000000000000000003845787472696e736963436f756e7400000c75333204000040416c6c45787472696e736963734c656e00000c75333204000024426c6f636b48617368010138543a3a426c6f636b4e756d6265721c543a3a48617368800000000000000000000000000000000000000000000000000000000000000000003445787472696e7369634461746101010c7533321c5665633c75383e0400002852616e646f6d5365656401001c543a3a4861736880000000000000000000000000000000000000000000000000000000000000000000184e756d626572010038543a3a426c6f636b4e756d626572200000000000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801001c543a3a48617368800000000000000000000000000000000000000000000000000000000000000000003845787472696e73696373526f6f7401001c543a3a486173688000000000000000000000000000000000000000000000000000000000000000000018446967657374010024543a3a446967657374040000184576656e74730100685665633c4576656e745265636f72643c543a3a4576656e743e3e0400000001084045787472696e7369635375636365737300049420416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656400045420416e2065787472696e736963206661696c65642e1061757261000000002474696d657374616d702454696d657374616d70010c0c4e6f77010024543a3a4d6f6d656e7420000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2c426c6f636b506572696f64010024543a3a4d6f6d656e7420050000000000000004c420546865206d696e696d756d2028616e6420616476697365642920706572696f64206265747765656e20626c6f636b732e24446964557064617465010010626f6f6c040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f01040c736574040c6e6f7748436f6d706163743c543a3a4d6f6d656e743e205820536574207468652063757272656e742074696d652e00750120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e2070686173652cbc20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e008501205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e74207370656369666965642062792060626c6f636b5f706572696f64602e0420d820546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e74602e0024636f6e73656e73757324436f6e73656e73757301044c4f726967696e616c417574686f7269746965730000485665633c543a3a53657373696f6e4b65793e0400000118487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e306e6f74655f6f66666c696e65041c6f66666c696e65f43c543a3a496e686572656e744f66666c696e655265706f727420617320496e686572656e744f66666c696e655265706f72743e3a3a496e686572656e74044501204e6f7465207468652070726576696f757320626c6f636b27732076616c696461746f72206d6973736564207468656972206f70706f7274756e69747920746f2070726f706f7365206120626c6f636b2e1872656d61726b041c5f72656d61726b1c5665633c75383e046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040c6e65771c5665633c75383e04482053657420746865206e657720636f64652e2c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e001c696e64696365731c496e646963657301082c4e657874456e756d53657401003c543a3a4163636f756e74496e6465781000000000047c20546865206e657874206672656520656e756d65726174696f6e207365742e1c456e756d53657401013c543a3a4163636f756e74496e646578445665633c543a3a4163636f756e7449643e040004582054686520656e756d65726174696f6e20736574732e010001043c4e65774163636f756e74496e64657808244163636f756e744964304163636f756e74496e64657810882041206e6577206163636f756e7420696e646578207761732061737369676e65642e0005012054686973206576656e74206973206e6f7420747269676765726564207768656e20616e206578697374696e6720696e64657820697320726561737369676e65646020746f20616e6f7468657220604163636f756e744964602e2062616c616e6365732042616c616e636573012034546f74616c49737375616e6365010028543a3a42616c616e6365400000000000000000000000000000000004a42054686520746f74616c20616d6f756e74206f66207374616b65206f6e207468652073797374656d2e484578697374656e7469616c4465706f736974010028543a3a42616c616e6365400000000000000000000000000000000004d020546865206d696e696d756d20616d6f756e7420616c6c6f77656420746f206b65657020616e206163636f756e74206f70656e2e2c5472616e73666572466565010028543a3a42616c616e636540000000000000000000000000000000000494205468652066656520726571756972656420746f206d616b652061207472616e736665722e2c4372656174696f6e466565010028543a3a42616c616e63654000000000000000000000000000000000042501205468652066656520726571756972656420746f2063726561746520616e206163636f756e742e204174206c6561737420617320626967206173205265636c61696d5265626174652e1c56657374696e67000130543a3a4163636f756e7449646c56657374696e675363686564756c653c543a3a42616c616e63653e040004d820496e666f726d6174696f6e20726567617264696e67207468652076657374696e67206f66206120676976656e206163636f756e742e2c4672656542616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e636540000000000000000000000000000000002c9c20546865202766726565272062616c616e6365206f66206120676976656e206163636f756e742e004d01205468697320697320746865206f6e6c792062616c616e63652074686174206d61747465727320696e207465726d73206f66206d6f7374206f7065726174696f6e73206f6e20746f6b656e732e204974206973690120616c6f6e65207573656420746f2064657465726d696e65207468652062616c616e6365207768656e20696e2074686520636f6e747261637420657865637574696f6e20656e7669726f6e6d656e742e205768656e207468697355012062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e20746865202763757272656e74206163636f756e74272069733d012064656c657465643a207370656369666963616c6c7920604672656542616c616e6365602e20467572746865726d6f72652c20604f6e4672656542616c616e63655a65726f602063616c6c6261636b410120697320696e766f6b65642c20676976696e672061206368616e636520746f2065787465726e616c206d6f64756c657320746f20636c65616e75702064617461206173736f636961746564207769746854207468652064656c65746564206163636f756e742e005d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c657465642069662060526573657276656442616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e3c526573657276656442616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e636540000000000000000000000000000000003075012054686520616d6f756e74206f66207468652062616c616e6365206f66206120676976656e206163636f756e7420746861742069732065787465726e616c6c792072657365727665643b20746869732063616e207374696c6c206765749c20736c61736865642c20627574206765747320736c6173686564206c617374206f6620616c6c2e006d0120546869732062616c616e63652069732061202772657365727665272062616c616e63652074686174206f746865722073756273797374656d732075736520696e206f7264657220746f2073657420617369646520746f6b656e737101207468617420617265207374696c6c20276f776e65642720627920746865206163636f756e7420686f6c6465722c20627574207768696368206172652073757370656e6461626c652e20285468697320697320646966666572656e74290120616e642077686f6c6c7920756e72656c6174656420746f207468652060426f6e64616765602073797374656d207573656420696e20746865207374616b696e67206d6f64756c652e29007501205768656e20746869732062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e2074686973202772657365727665206163636f756e7427b42069732064656c657465643a207370656369666963616c6c792c2060526573657276656442616c616e6365602e004d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c6574656420696620604672656542616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e144c6f636b73010130543a3a4163636f756e744964b05665633c42616c616e63654c6f636b3c543a3a42616c616e63652c20543a3a426c6f636b4e756d6265723e3e040004b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e0108207472616e736665720810646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c75654c436f6d706163743c543a3a42616c616e63653e04d4205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572207374616b65722e2c7365745f62616c616e63650c0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636510667265654c436f6d706163743c543a3a42616c616e63653e2072657365727665644c436f6d706163743c543a3a42616c616e63653e049420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e010c284e65774163636f756e7408244163636f756e7449641c42616c616e6365046c2041206e6577206163636f756e742077617320637265617465642e345265617065644163636f756e7404244163636f756e744964045c20416e206163636f756e7420776173207265617065642e205472616e7366657210244163636f756e744964244163636f756e7449641c42616c616e63651c42616c616e636504b0205472616e7366657220737563636565646564202866726f6d2c20746f2c2076616c75652c2066656573292e1c73657373696f6e1c53657373696f6e01202856616c696461746f72730100445665633c543a3a4163636f756e7449643e0400047c205468652063757272656e7420736574206f662076616c696461746f72732e3453657373696f6e4c656e677468010038543a3a426c6f636b4e756d62657220e803000000000000047c2043757272656e74206c656e677468206f66207468652073657373696f6e2e3043757272656e74496e646578010038543a3a426c6f636b4e756d62657220000000000000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e3043757272656e745374617274010024543a3a4d6f6d656e7420000000000000000004a02054696d657374616d70207768656e2063757272656e742073657373696f6e20737461727465642e44466f7263696e674e657753657373696f6e000010626f6f6c0400087901204e65772073657373696f6e206973206265696e6720666f72636564206973207468697320656e747279206578697374733b20696e20776869636820636173652c2074686520626f6f6c65616e2076616c75652069732077686574686572810120746865206e65772073657373696f6e2073686f756c6420626520636f6e736964657265642061206e6f726d616c20726f746174696f6e202872657761726461626c6529206f7220657863657074696f6e616c2028736c61736861626c65292e404c6173744c656e6774684368616e6765000038543a3a426c6f636b4e756d626572040004c020426c6f636b206174207768696368207468652073657373696f6e206c656e677468206c617374206368616e6765642e284e6578744b6579466f72000130543a3a4163636f756e74496434543a3a53657373696f6e4b65790400049020546865206e657874206b657920666f72206120676976656e2076616c696461746f722e444e65787453657373696f6e4c656e677468000038543a3a426c6f636b4e756d6265720400046420546865206e6578742073657373696f6e206c656e6774682e010c1c7365745f6b6579040c6b657934543a3a53657373696f6e4b65790861012053657473207468652073657373696f6e206b6579206f6620605f76616c696461746f726020746f20605f6b6579602e205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e657874242073657373696f6e2e287365745f6c656e677468040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e046d01205365742061206e65772073657373696f6e206c656e6774682e20576f6e2774206b69636b20696e20756e74696c20746865206e6578742073657373696f6e206368616e6765202861742063757272656e74206c656e677468292e44666f7263655f6e65775f73657373696f6e04346170706c795f7265776172647310626f6f6c045820466f726365732061206e65772073657373696f6e2e0104284e657753657373696f6e042c426c6f636b4e756d626572085501204e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f742074686520626c6f636b88206e756d626572206173207468652074797065206d6967687420737567676573742e1c7374616b696e671c5374616b696e6701643856616c696461746f72436f756e7401000c753332100000000004a82054686520696465616c206e756d626572206f66207374616b696e67207061727469636970616e74732e544d696e696d756d56616c696461746f72436f756e7401000c7533321004000000044101204d696e696d756d206e756d626572206f66207374616b696e67207061727469636970616e7473206265666f726520656d657267656e637920636f6e646974696f6e732061726520696d706f7365642e3853657373696f6e73506572457261010038543a3a426c6f636b4e756d62657220e80300000000000004a420546865206c656e677468206f662061207374616b696e672065726120696e2073657373696f6e732e3453657373696f6e52657761726401001c50657262696c6c103c000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e304f66666c696e65536c61736801001c50657262696c6c1040420f0004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e444f66666c696e65536c617368477261636501000c7533321000000000043901204e756d626572206f6620696e7374616e636573206f66206f66666c696e65207265706f727473206265666f726520736c617368696e6720626567696e7320666f722076616c696461746f72732e3c426f6e64696e674475726174696f6e010038543a3a426c6f636b4e756d62657220e80300000000000004b820546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20626c6f636b732e30496e76756c657261626c65730100445665633c543a3a4163636f756e7449643e04000034496e76756c6e657261626c65730100445665633c543a3a4163636f756e7449643e040008a50120416e792076616c696461746f72732074686174206d6179206e6576657220626520736c6173686564206f7220666f726369626c79206b69636b65642e20497427732061205665632073696e63652074686579277265206561737920746f20696e697469616c697365ad0120616e642074686520706572666f726d616e636520686974206973206d696e696d616c2028776520657870656374206e6f206d6f7265207468616e20666f757220696e76756c6e657261626c65732920616e64207265737472696374656420746f20746573746e6574732e18426f6e646564000130543a3a4163636f756e74496430543a3a4163636f756e7449640400040101204d61702066726f6d20616c6c206c6f636b65642022737461736822206163636f756e747320746f2074686520636f6e74726f6c6c6572206163636f756e742e184c6564676572000130543a3a4163636f756e744964e45374616b696e674c65646765723c543a3a4163636f756e7449642c2042616c616e63654f663c543e2c20543a3a426c6f636b4e756d6265723e0400044501204d61702066726f6d20616c6c2028756e6c6f636b6564292022636f6e74726f6c6c657222206163636f756e747320746f2074686520696e666f20726567617264696e6720746865207374616b696e672e145061796565010130543a3a4163636f756e7449644452657761726444657374696e6174696f6e040004a42057686572652074686520726577617264207061796d656e742073686f756c64206265206d6164652e2856616c696461746f7273010130543a3a4163636f756e7449647056616c696461746f7250726566733c42616c616e63654f663c543e3e080c000cec2054686520736574206f66206b6579732061726520616c6c20636f6e74726f6c6c65727320746861742077616e7420746f2076616c69646174652e00d4205468652076616c756573206172652074686520707265666572656e636573207468617420612076616c696461746f72206861732e284e6f6d696e61746f7273010130543a3a4163636f756e744964445665633c543a3a4163636f756e7449643e04000cec2054686520736574206f66206b6579732061726520616c6c20636f6e74726f6c6c65727320746861742077616e7420746f206e6f6d696e6174652e007c205468652076616c75652061726520746865206e6f6d696e6174696f6e732e1c5374616b657273010130543a3a4163636f756e744964904578706f737572653c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e0c00000008b101204e6f6d696e61746f727320666f72206120706172746963756c6172206163636f756e74207468617420697320696e20616374696f6e207269676874206e6f772e20596f752063616e27742069746572617465207468726f7567682076616c696461746f727320686572652cc02062757420796f752063616e2066696e64207468656d20696e20746865206073657373696f6e7360206d6f64756c652e2843757272656e74457261010038543a3a426c6f636b4e756d626572200000000000000000045c205468652063757272656e742065726120696e6465782e5043757272656e7453657373696f6e52657761726401003042616c616e63654f663c543e4000000000000000000000000000000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e4c43757272656e744f66666c696e65536c61736801003042616c616e63654f663c543e400000000000000000000000000000000004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e4043757272656e7445726152657761726401003042616c616e63654f663c543e40000000000000000000000000000000000869012054686520616363756d756c617465642072657761726420666f72207468652063757272656e74206572612e20526573657420746f207a65726f2061742074686520626567696e6e696e67206f66207468652065726120616e64cc20696e6372656173656420666f72206576657279207375636365737366756c6c792066696e69736865642073657373696f6e2e484e65787453657373696f6e73506572457261000038543a3a426c6f636b4e756d6265720400049020546865206e6578742076616c7565206f662073657373696f6e7320706572206572612e4c4c6173744572614c656e6774684368616e6765010038543a3a426c6f636b4e756d62657220000000000000000004e0205468652073657373696f6e20696e6465782061742077686963682074686520657261206c656e677468206c617374206368616e6765642e24536c6f745374616b6501003042616c616e63654f663c543e40000000000000000000000000000000000c31012054686520616d6f756e74206f662062616c616e6365206163746976656c79206174207374616b6520666f7220656163682076616c696461746f7220736c6f742c2063757272656e746c792e00c02054686973206973207573656420746f20646572697665207265776172647320616e642070756e6973686d656e74732e28536c617368436f756e74010130543a3a4163636f756e7449640c753332100000000004d10120546865206e756d626572206f662074696d6573206120676976656e2076616c696461746f7220686173206265656e207265706f72746564206f66666c696e652e205468697320676574732064656372656d656e746564206279206f6e652065616368206572612074686174207061737365732e34466f7263696e674e65774572610000082829040004682057652061726520666f7263696e672061206e6577206572612e3c526563656e746c794f66666c696e650100a05665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d6265722c20753332293e040004f101204d6f737420726563656e742060524543454e545f4f46464c494e455f434f554e546020696e7374616e6365732e202877686f206974207761732c207768656e20697420776173207265706f727465642c20686f77206d616e7920696e7374616e63657320746865792077657265206f66666c696e6520666f72292e013810626f6e640c28636f6e74726f6c6c65728c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c756554436f6d706163743c42616c616e63654f663c543e3e1470617965654452657761726444657374696e6174696f6e0881012054616b6520746865206f726967696e206163636f756e74206173206120737461736820616e64206c6f636b207570206076616c756560206f66206974732062616c616e63652e2060636f6e74726f6c6c6572602077696c6c2062652074686568206163636f756e74207468617420636f6e74726f6c732069742e28626f6e645f657874726104386d61785f6164646974696f6e616c3042616c616e63654f663c543e1875012041646420736f6d6520657874726120616d6f756e742074686174206861766520617070656172656420696e207468652073746173682060667265655f62616c616e63656020696e746f207468652062616c616e636520757020666f7224207374616b696e672e00510120557365207468697320696620746865726520617265206164646974696f6e616c2066756e647320696e20796f7572207374617368206163636f756e74207468617420796f75207769736820746f20626f6e642e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e18756e626f6e64041476616c756554436f6d706163743c42616c616e63654f663c543e3e285501205363686564756c65206120706f7274696f6e206f662074686520737461736820746f20626520756e6c6f636b656420726561647920666f72207472616e73666572206f75742061667465722074686520626f6e64010120706572696f6420656e64732e2049662074686973206c656176657320616e20616d6f756e74206163746976656c7920626f6e646564206c657373207468616e350120543a3a43757272656e63793a3a6578697374656e7469616c5f6465706f73697428292c207468656e20697420697320696e6372656173656420746f207468652066756c6c20616d6f756e742e004901204f6e63652074686520756e6c6f636b20706572696f6420697320646f6e652c20796f752063616e2063616c6c206077697468647261775f756e626f6e6465646020746f2061637475616c6c79206d6f7665c0207468652066756e6473206f7574206f66206d616e6167656d656e7420726561647920666f72207472616e736665722e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e00982053656520616c736f205b6043616c6c3a3a77697468647261775f756e626f6e646564605d2e4477697468647261775f756e626f6e64656400202d012052656d6f766520616e7920756e6c6f636b6564206368756e6b732066726f6d207468652060756e6c6f636b696e67602071756575652066726f6d206f7572206d616e6167656d656e742e003501205468697320657373656e7469616c6c7920667265657320757020746861742062616c616e636520746f206265207573656420627920746865207374617368206163636f756e7420746f20646f4c2077686174657665722069742077616e74732e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e006c2053656520616c736f205b6043616c6c3a3a756e626f6e64605d2e2076616c6964617465041470726566737056616c696461746f7250726566733c42616c616e63654f663c543e3e14e8204465636c617265207468652064657369726520746f2076616c696461746520666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e206e6f6d696e617465041c74617267657473a05665633c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263653e141101204465636c617265207468652064657369726520746f206e6f6d696e6174652060746172676574736020666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e146368696c6c0014c8204465636c617265206e6f2064657369726520746f206569746865722076616c6964617465206f72206e6f6d696e6174652e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e247365745f7061796565041470617965654452657761726444657374696e6174696f6e14b8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00fc204e4f54453a20546869732063616c6c206d757374206265206d6164652062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e507365745f73657373696f6e735f7065725f657261040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04982053657420746865206e756d626572206f662073657373696f6e7320696e20616e206572612e507365745f626f6e64696e675f6475726174696f6e040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04b020546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20657261732e4c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e34666f7263655f6e65775f65726104346170706c795f7265776172647310626f6f6c083d0120466f72636520746865726520746f2062652061206e6577206572612e205468697320616c736f20666f726365732061206e65772073657373696f6e20696d6d6564696174656c792061667465722e250120606170706c795f72657761726473602073686f756c64206265207472756520666f722076616c696461746f727320746f20676574207468652073657373696f6e207265776172642e5c7365745f6f66666c696e655f736c6173685f6772616365040c6e657730436f6d706163743c7533323e04902053657420746865206f66666c696e6520736c61736820677261636520706572696f642e447365745f696e76756c6e657261626c6573042876616c696461746f7273445665633c543a3a4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e010c18526577617264041c42616c616e636504e020416c6c2076616c696461746f72732068617665206265656e2072657761726465642062792074686520676976656e2062616c616e63652e384f66666c696e655761726e696e6708244163636f756e7449640c753332085501204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20676976656e2061206f66666c696e652d7761726e696e67202874686579277265207374696c6c15012077697468696e207468656972206772616365292e205468652061636372756564206e756d626572206f6620736c6173686573206973207265636f726465642c20746f6f2e304f66666c696e65536c61736808244163636f756e7449641c42616c616e6365042d01204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20736c61736865642062792074686520676976656e20616d6f756e742e2464656d6f63726163792444656d6f637261637901383c5075626c696350726f70436f756e7401002450726f70496e646578100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100ac5665633c2850726f70496e6465782c20543a3a50726f706f73616c2c20543a3a4163636f756e744964293e0400048020546865207075626c69632070726f706f73616c732e20556e736f727465642e244465706f7369744f6600012450726f70496e646578842842616c616e63654f663c543e2c205665633c543a3a4163636f756e7449643e29040004842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e304c61756e6368506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e384d696e696d756d4465706f73697401003042616c616e63654f663c543e400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e2c5075626c696344656c6179010038543a3a426c6f636b4e756d62657220000000000000000004d4205468652064656c6179206265666f726520656e6163746d656e7420666f7220616c6c207075626c6963207265666572656e64612e384d61784c6f636b506572696f647301002c4c6f636b506572696f6473040004d90120546865206d6178696d756d206e756d626572206f66206164646974696f6e616c206c6f636b20706572696f6473206120766f746572206d6179206f6666657220746f20737472656e677468656e20746865697220766f74652e204d756c7469706c6573206f6620605075626c696344656c6179602e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e3c5265666572656e64756d436f756e7401003c5265666572656e64756d496e646578100000000004390120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e64756d73207374617274656420736f206661722e244e65787454616c6c7901003c5265666572656e64756d496e646578100000000004c820546865206e657874207265666572656e64756d20696e64657820746861742073686f756c642062652074616c6c6965642e405265666572656e64756d496e666f4f6600013c5265666572656e64756d496e646578b4285265666572656e64756d496e666f3c543a3a426c6f636b4e756d6265722c20543a3a50726f706f73616c3e29040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e3444697370617463685175657565010138543a3a426c6f636b4e756d626572ac5665633c4f7074696f6e3c28543a3a50726f706f73616c2c205265666572656e64756d496e646578293e3e040004c0205175657565206f66207375636365737366756c207265666572656e646120746f20626520646973706174636865642e24566f74657273466f7201013c5265666572656e64756d496e646578445665633c543a3a4163636f756e7449643e040004a4204765742074686520766f7465727320666f72207468652063757272656e742070726f706f73616c2e18566f74654f6601017c285265666572656e64756d496e6465782c20543a3a4163636f756e7449642910566f746504000cd501204765742074686520766f746520696e206120676976656e207265666572656e64756d206f66206120706172746963756c617220766f7465722e2054686520726573756c74206973206d65616e696e6766756c206f6e6c792069662060766f746572735f666f726020696e636c7564657320746865c90120766f746572207768656e2063616c6c6564207769746820746865207265666572656e64756d2028796f75276c6c20676574207468652064656661756c742060566f7465602076616c7565206f7468657277697365292e20496620796f7520646f6e27742077616e7420746f20636865636b61012060766f746572735f666f72602c207468656e20796f752063616e20616c736f20636865636b20666f722073696d706c65206578697374656e636520776974682060566f74654f663a3a657869737473602066697273742e01181c70726f706f7365082070726f706f73616c40426f783c543a3a50726f706f73616c3e1476616c756554436f6d706163743c42616c616e63654f663c543e3e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e10766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f746508350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e4073746172745f7265666572656e64756d0c2070726f706f73616c40426f783c543a3a50726f706f73616c3e247468726573686f6c6434566f74655468726573686f6c641464656c617938543a3a426c6f636b4e756d62657204502053746172742061207265666572656e64756d2e4463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e3463616e63656c5f71756575656408107768656e5c436f6d706163743c543a3a426c6f636b4e756d6265723e14776869636830436f6d706163743c7533323e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e011c2050726f706f736564082450726f70496e6465781c42616c616e636500185461626c65640c2450726f70496e6465781c42616c616e6365385665633c4163636f756e7449643e001c53746172746564083c5265666572656e64756d496e64657834566f74655468726573686f6c640018506173736564043c5265666572656e64756d496e64657800244e6f74506173736564043c5265666572656e64756d496e646578002443616e63656c6c6564043c5265666572656e64756d496e64657800204578656375746564083c5265666572656e64756d496e64657810626f6f6c001c636f756e63696c1c436f756e63696c01503443616e646964616379426f6e6401003042616c616e63654f663c543e400900000000000000000000000000000004050120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f207375626d6974206f6e6527732063616e6469646163792e28566f74696e67426f6e6401003042616c616e63654f663c543e400000000000000000000000000000000004090120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f2062652061626c6520746f207375626d697420766f7465732e5050726573656e74536c617368506572566f74657201003042616c616e63654f663c543e4001000000000000000000000000000000040d01205468652070756e6973686d656e742c2070657220766f7465722c20696620796f752070726f7669646520616e20696e76616c69642070726573656e746174696f6e2e284361727279436f756e7401000c753332100200000004350120486f77206d616e792072756e6e6572732d75702073686f756c64206861766520746865697220617070726f76616c73207065727369737420756e74696c20746865206e65787420766f74652e5050726573656e746174696f6e4475726174696f6e010038543a3a426c6f636b4e756d62657220e803000000000000043d0120486f77206c6f6e6720746f2067697665206561636820746f702063616e64696461746520746f2070726573656e74207468656d73656c7665732061667465722074686520766f746520656e64732e4c496e6163746976654772616365506572696f64010024566f7465496e646578100100000008a10120486f77206d616e7920766f746520696e6465786573206e65656420746f20676f20627920616674657220612074617267657420766f7465722773206c61737420766f7465206265666f726520746865792063616e206265207265617065642069662074686569725020617070726f76616c7320617265206d6f6f742e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e305465726d4475726174696f6e010038543a3a426c6f636b4e756d626572200500000000000000049820486f77206c6f6e67206561636820706f736974696f6e2069732061637469766520666f722e3044657369726564536561747301000c753332100000000004e8204e756d626572206f66206163636f756e747320746861742073686f756c642062652073697474696e67206f6e2074686520636f756e63696c2e34416374697665436f756e63696c01008c5665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d626572293e0400106d01205468652063757272656e7420636f756e63696c2e205768656e2074686572652773206120766f746520676f696e67206f6e2c20746869732073686f756c64207374696c6c206265207573656420666f72206578656375746976657101206d6174746572732e2054686520626c6f636b206e756d62657220287365636f6e6420656c656d656e7420696e20746865207475706c65292069732074686520626c6f636b207468617420746865697220706f736974696f6e20697371012061637469766520756e74696c202863616c63756c61746564206279207468652073756d206f662074686520626c6f636b206e756d626572207768656e2074686520636f756e63696c206d656d6265722077617320656c65637465646820616e64207468656972207465726d206475726174696f6e292e24566f7465436f756e74010024566f7465496e64657810000000000405012054686520746f74616c206e756d626572206f6620766f746573207468617420686176652068617070656e6564206f722061726520696e2070726f67726573732e2c417070726f76616c734f66010130543a3a4163636f756e744964245665633c626f6f6c3e0400086d012041206c697374206f6620766f74657320666f72206561636820766f7465722c2072657370656374696e6720746865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f7465722077617340206c617374206163746976652061742e385265676973746572496e666f4f66000130543a3a4163636f756e7449644028566f7465496e6465782c20753332290400087d012054686520766f746520696e64657820616e64206c69737420736c6f742074686174207468652063616e646964617465206077686f60207761732072656769737465726564206f7220604e6f6e6560206966207468657920617265206e6f74582063757272656e746c7920726567697374657265642e304c6173744163746976654f66000130543a3a4163636f756e74496424566f7465496e646578040004010120546865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f74657220776173206c617374206163746976652061742e18566f746572730100445665633c543a3a4163636f756e7449643e04000460205468652070726573656e7420766f746572206c6973742e2843616e646964617465730100445665633c543a3a4163636f756e7449643e04000470205468652070726573656e742063616e646964617465206c6973742e3843616e646964617465436f756e7401000c753332100000000000304e65787446696e616c6973650000a028543a3a426c6f636b4e756d6265722c207533322c205665633c543a3a4163636f756e7449643e29040004210120546865206163636f756e747320686f6c64696e672074686520736561747320746861742077696c6c206265636f6d652066726565206f6e20746865206e6578742074616c6c792e40536e617073686f7465645374616b65730100445665633c42616c616e63654f663c543e3e040004e820546865207374616b6573206173207468657920776572652061742074686520706f696e7420746861742074686520766f746520656e6465642e2c4c6561646572626f6172640000845665633c2842616c616e63654f663c543e2c20543a3a4163636f756e744964293e040004e02047657420746865206c6561646572626f6172642069662077653b726520696e207468652070726573656e746174696f6e2070686173652e0124347365745f617070726f76616c730814766f746573245665633c626f6f6c3e14696e64657848436f6d706163743c566f7465496e6465783e086101205365742063616e64696461746520617070726f76616c732e20417070726f76616c20736c6f747320737461792076616c6964206173206c6f6e672061732063616e6469646174657320696e2074686f736520736c6f7473402061726520726567697374657265642e4c726561705f696e6163746976655f766f74657210387265706f727465725f696e64657830436f6d706163743c7533323e0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263652477686f5f696e64657830436f6d706163743c7533323e48617373756d65645f766f74655f696e64657848436f6d706163743c566f7465496e6465783e1461012052656d6f7665206120766f7465722e20466f72206974206e6f7420746f206265206120626f6e642d636f6e73756d696e67206e6f2d6f702c20616c6c20617070726f7665642063616e64696461746520696e64696365737101206d757374206e6f772062652065697468657220756e72656769737465726564206f72207265676973746572656420746f20612063616e646964617465207468617420726567697374657265642074686520736c6f74206166746572a02074686520766f7465722067617665207468656972206c61737420617070726f76616c207365742e000101204d61792062652063616c6c656420627920616e796f6e652e2052657475726e732074686520766f746572206465706f73697420746f20607369676e6564602e34726574726163745f766f7465720414696e64657830436f6d706163743c7533323e042d012052656d6f7665206120766f7465722e20416c6c20766f746573206172652063616e63656c6c656420616e642074686520766f746572206465706f7369742069732072657475726e65642e407375626d69745f63616e6469646163790410736c6f7430436f6d706163743c7533323e0c78205375626d6974206f6e6573656c6620666f722063616e6469646163792e001101204163636f756e74206d757374206861766520656e6f756768207472616e736665727261626c652066756e647320696e20697420746f207061792074686520626f6e642e3870726573656e745f77696e6e65720c2463616e6469646174658c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514746f74616c54436f6d706163743c42616c616e63654f663c543e3e14696e64657848436f6d706163743c566f7465496e6465783e0c650120436c61696d207468617420607369676e656460206973206f6e65206f662074686520746f702053656c663a3a63617272795f636f756e742829202b2063757272656e745f766f746528292e312063616e646964617465732ea501204f6e6c7920776f726b73206966207468652060626c6f636b5f6e756d626572203e3d2063757272656e745f766f746528292e306020616e6420603c2063757272656e745f766f746528292e30202b2070726573656e746174696f6e5f6475726174696f6e282960607820607369676e6564602073686f756c642068617665206174206c65617374447365745f646573697265645f73656174730414636f756e7430436f6d706163743c7533323e0c650120536574207468652064657369726564206d656d62657220636f756e743b206966206c6f776572207468616e207468652063757272656e7420636f756e742c207468656e2073656174732077696c6c206e6f74206265207570690120656c656374696f6e207768656e2074686579206578706972652e204966206d6f72652c207468656e2061206e657720766f74652077696c6c2062652073746172746564206966206f6e65206973206e6f7420616c72656164793420696e2070726f67726573732e3472656d6f76655f6d656d626572040c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c71012052656d6f7665206120706172746963756c6172206d656d6265722e20412074616c6c792077696c6c2068617070656e20696e7374616e746c7920286966206e6f7420616c726561647920696e20612070726573656e746174696f6e410120706572696f642920746f2066696c6c2074686520736561742069662072656d6f76616c206d65616e732074686174207468652064657369726564206d656d6265727320617265206e6f74206d65742e7c20546869732069732065666665637469766520696d6d6564696174656c792e647365745f70726573656e746174696f6e5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08590120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e746c79206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e447365745f7465726d5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08510120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e74206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e01102c566f74657252656170656408244163636f756e744964244163636f756e74496404542072656170656420766f7465722c2072656170657240426164526561706572536c617368656404244163636f756e744964043c20736c6173686564207265617065723054616c6c7953746172746564040c75333204f420412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320737461727465642e3854616c6c7946696e616c6973656408385665633c4163636f756e7449643e385665633c4163636f756e7449643e04690120412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320656e646564202877697468206f6e65206f72206d6f7265206e6577206d656d62657273292e38636f756e63696c5f766f74696e6734436f756e63696c566f74696e67012034436f6f6c6f6666506572696f64010038543a3a426c6f636b4e756d62657220e8030000000000000030566f74696e67506572696f64010038543a3a426c6f636b4e756d6265722003000000000000000040456e61637444656c6179506572696f64010038543a3a426c6f636b4e756d62657220000000000000000004cd01204e756d626572206f6620626c6f636b7320627920776869636820746f2064656c617920656e6163746d656e74206f66207375636365737366756c2c206e6f6e2d756e616e696d6f75732d636f756e63696c2d696e7374696761746564207265666572656e64756d2070726f706f73616c732e2450726f706f73616c730100785665633c28543a3a426c6f636b4e756d6265722c20543a3a48617368293e0400002850726f706f73616c4f6600011c543a3a486173682c543a3a50726f706f73616c0400003850726f706f73616c566f7465727301011c543a3a48617368445665633c543a3a4163636f756e7449643e04000034436f756e63696c566f74654f6600015c28543a3a486173682c20543a3a4163636f756e7449642910626f6f6c040000385665746f656450726f706f73616c00011c543a3a486173688c28543a3a426c6f636b4e756d6265722c205665633c543a3a4163636f756e7449643e2904000001141c70726f706f7365042070726f706f73616c40426f783c543a3a50726f706f73616c3e0010766f7465082070726f706f73616c1c543a3a486173681c617070726f766510626f6f6c00107665746f043470726f706f73616c5f686173681c543a3a4861736800487365745f636f6f6c6f66665f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e00447365745f766f74696e675f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e0001084054616c6c7943616e63656c6174696f6e1010486173680c7533320c7533320c753332080101204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d2063616e63656c6c6174696f6e20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c54616c6c795265666572656e64756d1010486173680c7533320c7533320c75333208cc204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c636f756e63696c5f6d6f74696f6e7338436f756e63696c4d6f74696f6e7301102450726f706f73616c730100305665633c543a3a486173683e04000498205468652028686173686573206f662920746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600011c543a3a48617368583c542061732054726169743e3a3a50726f706f73616c040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700011c543a3a48617368e82850726f706f73616c496e6465782c207533322c205665633c543a3a4163636f756e7449643e2c205665633c543a3a4163636f756e7449643e29040004250120566f74657320666f72206120676976656e2070726f706f73616c3a202872657175697265645f7965735f766f7465732c207965735f766f746572732c206e6f5f766f74657273292e3450726f706f73616c436f756e7401000c753332100000000004482050726f706f73616c7320736f206661722e01081c70726f706f736508247468726573686f6c6430436f6d706163743c7533323e2070726f706f73616c6c426f783c3c542061732054726169743e3a3a50726f706f73616c3e0010766f74650c2070726f706f73616c1c543a3a4861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c0001142050726f706f73656410244163636f756e7449643450726f706f73616c496e64657810486173680c7533320465012041206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e20753332292e14566f74656414244163636f756e744964104861736810626f6f6c0c7533320c7533320809012041206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e67f420612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e206173207533327320726573706563746976656c79292e20417070726f76656404104861736804c42041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404104861736804d42041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408104861736810626f6f6c0405012041206d6f74696f6e207761732065786563757465643b2060626f6f6c6020697320747275652069662072657475726e656420776974686f7574206572726f722e4066696e616c6974795f747261636b6572000001042866696e616c5f68696e74041068696e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08f42048696e7420746861742074686520617574686f72206f66207468697320626c6f636b207468696e6b732074686520626573742066696e616c697a65646c20626c6f636b2069732074686520676976656e206e756d6265722e001c6772616e6470613c4772616e64706146696e616c69747901083450656e64696e674368616e67650000c853746f72656450656e64696e674368616e67653c543a3a426c6f636b4e756d6265722c20543a3a53657373696f6e4b65793e040000284e657874466f72636564000038543a3a426c6f636b4e756d6265720400000104487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e0104384e6577417574686f72697469657304585665633c2853657373696f6e4b65792c20753634293e0490204e657720617574686f726974792073657420686173206265656e206170706c6965642e20747265617375727920547265617375727901203050726f706f73616c426f6e6401001c5065726d696c6c10000000000851012050726f706f7274696f6e206f662066756e647320746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c61636520612070726f706f73616c2e20416e206163636570746564dc2070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f65736e27742e4c50726f706f73616c426f6e644d696e696d756d01003042616c616e63654f663c543e4000000000000000000000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64010038543a3a426c6f636b4e756d626572200100000000000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e01001c5065726d696c6c10000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e0c506f7401003042616c616e63654f663c543e400000000000000000000000000000000004cc20546f74616c2066756e647320617661696c61626c6520746f2074686973206d6f64756c6520666f72207370656e64696e672e3450726f706f73616c436f756e7401003450726f706f73616c496e646578100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300013450726f706f73616c496e6465789050726f706f73616c3c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e0400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e24417070726f76616c730100485665633c50726f706f73616c496e6465783e040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e01143470726f706f73655f7370656e64081476616c756554436f6d706163743c42616c616e63654f663c543e3e2c62656e65666963696172798c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c2d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e1c7365745f706f74041c6e65775f706f7454436f6d706163743c42616c616e63654f663c543e3e04b420536574207468652062616c616e6365206f662066756e647320617661696c61626c6520746f207370656e642e24636f6e666967757265103470726f706f73616c5f626f6e6440436f6d706163743c5065726d696c6c3e5470726f706f73616c5f626f6e645f6d696e696d756d54436f6d706163743c42616c616e63654f663c543e3e307370656e645f706572696f645c436f6d706163743c543a3a426c6f636b4e756d6265723e106275726e40436f6d706163743c5065726d696c6c3e0470202852652d29636f6e6669677572652074686973206d6f64756c652e3c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e04fc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e40617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e085d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e01142050726f706f736564043450726f706f73616c496e6465780438204e65772070726f706f73616c2e205370656e64696e67041c42616c616e636504e8205765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c3450726f706f73616c496e6465781c42616c616e6365244163636f756e744964048020536f6d652066756e64732068617665206265656e20616c6c6f63617465642e144275726e74041c42616c616e6365048c20536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572041c42616c616e6365043101205370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e20636f6e747261637420436f6e7472616374012c2c436f6e7472616374466565010028543a3a42616c616e63654015000000000000000000000000000000044d01205468652066656520726571756972656420746f20637265617465206120636f6e74726163742e204174206c6561737420617320626967206173207374616b696e672773205265636c61696d5265626174652e2c43616c6c42617365466565010018543a3a47617320870000000000000004b02054686520666565206368617267656420666f7220612063616c6c20696e746f206120636f6e74726163742e3443726561746542617365466565010018543a3a47617320af0000000000000004b02054686520666565206368617267656420666f72206120637265617465206f66206120636f6e74726163742e204761735072696365010028543a3a42616c616e63654001000000000000000000000000000000047820546865207072696365206f66206f6e6520756e6974206f66206761732e204d6178446570746801000c753332106400000004c820546865206d6178696d756d206e657374696e67206c6576656c206f6620612063616c6c2f63726561746520737461636b2e34426c6f636b4761734c696d6974010018543a3a4761732040420f000000000004f020546865206d6178696d756d20616d6f756e74206f6620676173207468617420636f756c6420626520657870656e6465642070657220626c6f636b2e204761735370656e74010018543a3a476173200000000000000000048020476173207370656e7420736f2066617220696e207468697320626c6f636b2e3c43757272656e745363686564756c650100405363686564756c653c543a3a4761733ef000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000000001001000000004942043757272656e7420636f7374207363686564756c6520666f7220636f6e7472616374732e28436f6465486173684f66000130543a3a4163636f756e7449642c436f6465486173683c543e040004a82054686520636f6465206173736f6369617465642077697468206120676976656e206163636f756e742e305072697374696e65436f646500012c436f6465486173683c543e1c5665633c75383e04000465012041206d617070696e672066726f6d20616e206f726967696e616c20636f6465206861736820746f20746865206f726967696e616c20636f64652c20756e746f756368656420627920696e737472756d656e746174696f6e2e2c436f646553746f7261676500012c436f6465486173683c543e587761736d3a3a5072656661625761736d4d6f64756c6504000475012041206d617070696e67206265747765656e20616e206f726967696e616c20636f6465206861736820616e6420696e737472756d656e746564207761736d20636f64652c20726561647920666f722074686520657865637574696f6e2e01103c7570646174655f7363686564756c6504207363686564756c65405363686564756c653c543a3a4761733e0cb4205570646174657320746865207363686564756c6520666f72206d65746572696e6720636f6e7472616374732e000d0120546865207363686564756c65206d7573742068617665206120677265617465722076657273696f6e207468616e207468652073746f726564207363686564756c652e207075745f636f646508246761735f6c696d69743c436f6d706163743c543a3a4761733e10636f64651c5665633c75383e0445012053746f72657320636f646520696e207468652073746f726167652e20596f752063616e20696e7374616e746961746520636f6e747261637473206f6e6c7920776974682073746f72656420636f64652e1063616c6c1010646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c75654c436f6d706163743c543a3a42616c616e63653e246761735f6c696d69743c436f6d706163743c543a3a4761733e10646174611c5665633c75383e042901204d616b6520612063616c6c20746f206120737065636966696564206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e186372656174651024656e646f776d656e744c436f6d706163743c543a3a42616c616e63653e246761735f6c696d69743c436f6d706163743c543a3a4761733e24636f64655f686173682c436f6465486173683c543e10646174611c5665633c75383e244d01204372656174652061206e657720636f6e74726163742c206f7074696f6e616c6c79207472616e73666572696e6720736f6d652062616c616e636520746f207468652063726561746564206163636f756e742e0084204372656174696f6e20697320657865637574656420617320666f6c6c6f77733a004101202d207468652064657374696e6174696f6e206164647265737320697320636f6d7075746564206261736564206f6e207468652073656e64657220616e642068617368206f662074686520636f64652eb8202d206163636f756e7420697320637265617465642061742074686520636f6d707574656420616464726573732e6d01202d20746865206063746f725f636f64656020697320657865637574656420696e2074686520636f6e74657874206f6620746865206e65776c792063726561746564206163636f756e742e204275666665722072657475726e65645d0120202061667465722074686520657865637574696f6e206973207361766564206173207468652060636f646560206f6620746865206163636f756e742e205468617420636f64652077696c6c20626520696e766f6b6564b420202075706f6e20616e79206d6573736167652072656365697665642062792074686973206163636f756e742e0114205472616e736665720c244163636f756e744964244163636f756e7449641c42616c616e6365047501205472616e736665722068617070656e6564206066726f6d60202d3e2060746f60207769746820676976656e206076616c7565602061732070617274206f66206120606d6573736167652d63616c6c60206f722060637265617465602e30496e7374616e74696174656408244163636f756e744964244163636f756e74496404dc20436f6e7472616374206465706c6f7965642062792061646472657373206174207468652073706563696669656420616464726573732e28436f646553746f72656404104861736804b820436f646520776974682074686520737065636966696564206861736820686173206265656e2073746f7265642e3c5363686564756c6555706461746564040c75333204c020547269676765726564207768656e207468652063757272656e74207363686564756c6520697320757064617465642e284469737061746368656408244163636f756e74496410626f6f6c08390120412063616c6c2077617320646973706174636865642066726f6d2074686520676976656e206163636f756e742e2054686520626f6f6c207369676e616c7320776865746865722069742077617374207375636365737366756c20657865637574696f6e206f72206e6f742e107375646f105375646f01040c4b6579010030543a3a4163636f756e744964800000000000000000000000000000000000000000000000000000000000000000000108107375646f042070726f706f73616c40426f783c543a3a50726f706f73616c3e001c7365745f6b6579040c6e65778c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001081453756469640410626f6f6c04602041207375646f206a75737420746f6f6b20706c6163652e284b65794368616e67656404244163636f756e74496404f020546865207375646f6572206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c6965642e10666565731046656573010c485472616e73616374696f6e4261736546656501002841737365744f663c543e400000000000000000000000000000000004dc205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b2074686520626173652e485472616e73616374696f6e4279746546656501002841737365744f663c543e4000000000000000000000000000000000040d01205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b20746865207065722d6279746520706f7274696f6e2e5443757272656e745472616e73616374696f6e46656501010c7533322841737365744f663c543e400000000000000000000000000000000010150120546865206065787472696e7369635f696e646578203d3e20616363756d756c617465645f6665657360206d61702c20636f6e7461696e696e67207265636f72647320746fd420747261636b20746865206f766572616c6c2063686172676564206665657320666f722065616368207472616e73616374696f6e2e00c420416c6c207265636f7264732073686f756c642062652072656d6f7665642061742066696e616c6973652073746167652e0001041c43686172676564080c75333218416d6f756e7404a8204665652063686172676564202865787472696e7369635f696e6465782c206665655f616d6f756e7429'; + +export default meta; diff --git a/packages/types/src/Metadata/v1/toV0.ts b/packages/types/src/Metadata/v1/toV0.ts new file mode 100644 index 0000000000..8e2f4dc1d0 --- /dev/null +++ b/packages/types/src/Metadata/v1/toV0.ts @@ -0,0 +1,102 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { OuterDispatchCall, OuterDispatchMetadata } from '../v0/Calls'; +import { EventMetadata, OuterEventMetadata, OuterEventMetadataEvent } from '../v0/Events'; +import { CallMetadata, FunctionMetadata, RuntimeModuleMetadata, ModuleMetadata, StorageFunctionMetadata, StorageMetadata, StorageFunctionType } from '../v0/Modules'; + +import MetadataV0 from '../v0/Metadata'; +import MetadataV1, { MetadataModuleV1 } from '.'; + +function storageV0 (mod: MetadataModuleV1): StorageMetadata | null { + if (mod.storage.isNone) { + return null; + } + + return new StorageMetadata({ + prefix: mod.prefix, + functions: mod.storage.unwrap().map(({ docs, fallback, modifier, name, type }) => + new StorageFunctionMetadata({ + name, + modifier: modifier.toNumber(), + type: new StorageFunctionType(type), + default: fallback, + documentation: docs + }) + ) + }); +} + +function moduleV0 (mod: MetadataModuleV1): ModuleMetadata { + return new ModuleMetadata({ + name: 'Module', + call: new CallMetadata({ + name: 'Call', + functions: mod.calls.isNone + ? [] + : mod.calls.unwrap().map(({ args, docs, name }, id) => + new FunctionMetadata({ + id, + name, + arguments: args, + documentation: docs + }) + ) + }) + }); +} + +function modulesV0 (v1: MetadataV1): Array { + return v1.modules.map((mod) => + new RuntimeModuleMetadata({ + prefix: mod.name, // passed from name, compact with casing + module: moduleV0(mod), + storage: storageV0(mod) + }) + ); +} + +function outerDispatchV0 (v1: MetadataV1): OuterDispatchMetadata { + return new OuterDispatchMetadata({ + name: 'Call', + calls: v1.modules + .filter(({ calls }) => calls.isSome) + .map((mod, index) => + new OuterDispatchCall({ + // name and prefix are swapped in the actual V0 data, i.e. name is UpperCase + name: mod.prefix, + prefix: mod.name, + index + }) + ) + }); +} + +function outerEventV0 (v1: MetadataV1): OuterEventMetadata { + return new OuterEventMetadata({ + name: 'Event', + events: v1.modules + .filter(({ events }) => events.isSome) + .map((mod) => + new OuterEventMetadataEvent([ + mod.name, + mod.events.unwrap().map(({ args, docs, name }) => + new EventMetadata({ + name, + arguments: args, + documentation: docs + }) + ) + ]) + ) + }); +} + +export default function toV0 (v1: MetadataV1): MetadataV0 { + return new MetadataV0({ + outerEvent: outerEventV0(v1), + modules: modulesV0(v1), + outerDispatch: outerDispatchV0(v1) + }); +} diff --git a/packages/types/src/Metadata/v2/Metadata.spec.ts b/packages/types/src/Metadata/v2/Metadata.spec.ts new file mode 100644 index 0000000000..31454bec29 --- /dev/null +++ b/packages/types/src/Metadata/v2/Metadata.spec.ts @@ -0,0 +1,52 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsicsFromMeta from '@plugnet/extrinsics/fromMetadata'; + +import createType from '../../codec/createType'; +import Method from '../../primitive/Method'; + +import Metadata from '../Metadata'; +import latestParsed from './latest.substrate.v2.json'; +import rpcData from './static'; + +describe('MetadataV2', () => { + const metadata = new Metadata(rpcData); + + it('decodes latest properly', () => { + const str = JSON.stringify(metadata.toJSON()); + + expect(metadata.version).toBe(2); + expect(metadata.asV2.modules.length).not.toBe(0); + expect(str).toEqual(JSON.stringify(latestParsed)); + }); + + it('converts v2 to V1', () => { + const v1 = metadata.asV1; + + expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true)); + }); + + it('converts v2 to V0', () => { + const v1 = metadata.asV0; + + expect(metadata.getUniqTypes(true)).toEqual(v1.getUniqTypes(true)); + }); + + describe('storage with default values', () => { + Method.injectMethods(extrinsicsFromMeta(metadata.asV0)); + + metadata.asV2.modules + .filter(({ storage }) => storage.isSome) + .map((mod) => + mod.storage.unwrap().forEach(({ fallback, name, type }) => { + it(`creates default types for ${mod.prefix}.${name}, type ${type}`, () => { + expect( + () => createType(type.toString(), fallback) + ).not.toThrow(); + }); + }) + ); + }); +}); diff --git a/packages/types/src/Metadata/v2/Metadata.ts b/packages/types/src/Metadata/v2/Metadata.ts new file mode 100644 index 0000000000..ebdb43ae05 --- /dev/null +++ b/packages/types/src/Metadata/v2/Metadata.ts @@ -0,0 +1,130 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { MetadataInterface } from '../types'; + +import Option from '../../codec/Option'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import { flattenUniq, validateTypes } from '../util'; + +import { MetadataCall } from '../v1/Calls'; +import { MetadataEvent } from '../v1/Events'; +import { MetadataStorage } from './Storage'; + +/** + * @name MetadataModule + * @description + * The definition of a module in the system + */ +export class MetadataModule extends Struct { + constructor (value?: any) { + super({ + name: Text, + prefix: Text, + storage: Option.with(Vector.with(MetadataStorage)), + calls: Option.with(Vector.with(MetadataCall)), + events: Option.with(Vector.with(MetadataEvent)) + }, value); + } + + /** + * @description the module calls + */ + get calls (): Option> { + return this.get('calls') as Option>; + } + + /** + * @description the module events + */ + get events (): Option> { + return this.get('events') as Option>; + } + + /** + * @description the module name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description the module prefix + */ + get prefix (): Text { + return this.get('prefix') as Text; + } + + /** + * @description the associated module storage + */ + get storage (): Option> { + return this.get('storage') as Option>; + } +} + +/** + * @name MetadataV2 + * @description + * The runtime metadata as a decoded structure + */ +export default class MetadataV2 extends Struct implements MetadataInterface { + constructor (value?: any) { + super({ + modules: Vector.with(MetadataModule) + }, value); + } + + /** + * @description The associated modules for this structure + */ + get modules (): Vector { + return this.get('modules') as Vector; + } + + private get callNames () { + return this.modules.map((mod) => + mod.calls.isNone + ? [] + : mod.calls.unwrap().map((fn) => + fn.args.map((arg) => arg.type.toString()) + ) + ); + } + + private get eventNames () { + return this.modules.map((mod) => + mod.events.isNone + ? [] + : mod.events.unwrap().map((event) => + event.args.map((arg) => arg.toString()) + ) + ); + } + + private get storageNames () { + return this.modules.map((mod) => + mod.storage.isNone + ? [] + : mod.storage.unwrap().map((fn) => + fn.type.isMap + ? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()] + : [fn.type.asType.toString()] + ) + ); + } + + /** + * @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated + */ + getUniqTypes (throwError: boolean): Array { + const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]); + + validateTypes(types, throwError); + + return types; + } +} diff --git a/packages/types/src/Metadata/v2/Storage.ts b/packages/types/src/Metadata/v2/Storage.ts new file mode 100644 index 0000000000..e8f486e711 --- /dev/null +++ b/packages/types/src/Metadata/v2/Storage.ts @@ -0,0 +1,142 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../../codec/EnumType'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Bool from '../../primitive/Bool'; +import Bytes from '../../primitive/Bytes'; +import Null from '../../primitive/Null'; +import Text from '../../primitive/Text'; +import Type from '../../primitive/Type'; +import { MetadataStorageModifier } from '../v1/Storage'; + +export class Default extends Null {} + +export class Optional extends Null {} + +export class MapType extends Struct { + constructor (value?: any) { + super({ + key: Type, + value: Type, + isLinked: Bool + }, value); + } + + /** + * @description The mapped key as [[Type]] + */ + get key (): Type { + return this.get('key') as Type; + } + + /** + * @description The mapped value as [[Type]] + */ + get value (): Type { + return this.get('value') as Type; + } + + /** + * @description Is this an enumerable linked map + */ + get isLinked (): boolean { + return (this.get('isLinked') as Bool).valueOf(); + } +} + +export class PlainType extends Type { +} + +export class MetadataStorageType extends EnumType { + constructor (value?: any, index?: number) { + super({ + PlainType, + MapType + }, value, index); + } + + /** + * @description `true` if the storage entry is a map + */ + get isMap (): boolean { + return this.toNumber() === 1; + } + + /** + * @description The value as a mapped value + */ + get asMap (): MapType { + return this.value as MapType; + } + + /** + * @description The value as a [[Type]] value + */ + get asType (): PlainType { + return this.value as PlainType; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.isMap + ? this.asMap.value.toString() + : this.asType.toString(); + } +} + +/** + * @name MetadataModule + * @description + * The definition of a storage function + */ +export class MetadataStorage extends Struct { + constructor (value?: any) { + super({ + name: Text, + modifier: MetadataStorageModifier, + type: MetadataStorageType, + fallback: Bytes, + docs: Vector.with(Text) + }, value); + } + + /** + * @description The [[Text]] documentation + */ + get docs (): Vector { + return this.get('docs') as Vector; + } + + /** + * @description The [[Bytes]] fallback default + */ + get fallback (): Bytes { + return this.get('fallback') as Bytes; + } + + /** + * @description The [[MetadataArgument]] for arguments + */ + get modifier (): MetadataStorageModifier { + return this.get('modifier') as MetadataStorageModifier; + } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The [[MetadataStorageType]] + */ + get type (): MetadataStorageType { + return this.get('type') as MetadataStorageType; + } +} diff --git a/packages/types/src/Metadata/v2/index.ts b/packages/types/src/Metadata/v2/index.ts new file mode 100644 index 0000000000..652957147f --- /dev/null +++ b/packages/types/src/Metadata/v2/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as MetadataV2, MetadataModule as MetadataModuleV2 } from './Metadata'; + +export default MetadataV2; + +export { + MetadataModuleV2 +}; diff --git a/packages/types/src/Metadata/v2/latest.substrate.v2.json b/packages/types/src/Metadata/v2/latest.substrate.v2.json new file mode 100644 index 0000000000..6ca0b3a37a --- /dev/null +++ b/packages/types/src/Metadata/v2/latest.substrate.v2.json @@ -0,0 +1,3093 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "MetadataV2": { + "modules": [ + { + "name": "system", + "prefix": "System", + "storage": [ + { + "name": "AccountNonce", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Index", + "isLinked": false + } + }, + "fallback": "0x0000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Hash", + "isLinked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "MapType": { + "key": "u32", + "value": "Bytes", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "RandomSeed", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "ExtrinsicsRoot", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "PlainType": "Digest" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": null, + "events": [ + { + "name": "ExtrinsicSuccess", + "args": [], + "docs": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "args": [], + "docs": [ + " An extrinsic failed." + ] + } + ] + }, + { + "name": "aura", + "prefix": "", + "storage": null, + "calls": null, + "events": null + }, + { + "name": "timestamp", + "prefix": "Timestamp", + "storage": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0500000000000000", + "docs": [ + " The minimum (and advised) period between blocks." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " Did the timestamp get updated in this block?" + ] + } + ], + "calls": [ + { + "name": "set", + "args": [ + { + "name": "now", + "type": "Compact" + } + ], + "docs": [ + " Set the current time.", + "", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `block_period`.", + " ", + " The dispatch origin for this call must be `Inherent`." + ] + } + ], + "events": null + }, + { + "name": "consensus", + "prefix": "Consensus", + "storage": [ + { + "name": "OriginalAuthorities", + "modifier": "Optional", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehaviour." + ] + }, + { + "name": "note_offline", + "args": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "docs": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "name": "remark", + "args": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "docs": [ + " Make some on-chain remark." + ] + }, + { + "name": "set_heap_pages", + "args": [ + { + "name": "pages", + "type": "u64" + } + ], + "docs": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "name": "set_code", + "args": [ + { + "name": "new", + "type": "Bytes" + } + ], + "docs": [ + " Set the new code." + ] + }, + { + "name": "set_storage", + "args": [ + { + "name": "items", + "type": "Vec" + } + ], + "docs": [ + " Set some items of storage." + ] + } + ], + "events": null + }, + { + "name": "indices", + "prefix": "Indices", + "storage": [ + { + "name": "NextEnumSet", + "modifier": "Default", + "type": { + "PlainType": "AccountIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountIndex", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The enumeration sets." + ] + } + ], + "calls": [], + "events": [ + { + "name": "NewAccountIndex", + "args": [ + "AccountId", + "AccountIndex" + ], + "docs": [ + " A new account index was assigned.", + "", + " This event is not triggered when an existing index is reassigned", + " to another `AccountId`." + ] + } + ] + }, + { + "name": "balances", + "prefix": "Balances", + "storage": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total amount of stake on the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount allowed to keep an account open." + ] + }, + { + "name": "TransferFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to create an account. At least as big as ReclaimRebate." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VestingSchedule", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information regarding the vesting of a given account." + ] + }, + { + "name": "FreeBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance", + "isLinked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It is", + " alone used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Furthermore, `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to cleanup data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance", + "isLinked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable. (This is different", + " and wholly unrelated to the `Bondage` system used in the staking module.)", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Any liquidity locks on some account balances." + ] + } + ], + "calls": [ + { + "name": "transfer", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Transfer some liquid free balance to another staker." + ] + }, + { + "name": "set_balance", + "args": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "docs": [ + " Set the balances of a given account." + ] + } + ], + "events": [ + { + "name": "NewAccount", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "args": [ + "AccountId" + ], + "docs": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "docs": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + }, + { + "name": "session", + "prefix": "Session", + "storage": [ + { + "name": "Validators", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": "Optional", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "SessionKey", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next session length." + ] + } + ], + "calls": [ + { + "name": "set_key", + "args": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "docs": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "name": "set_length", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "name": "force_new_session", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Forces a new session." + ] + } + ], + "events": [ + { + "name": "NewSession", + "args": [ + "BlockNumber" + ], + "docs": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + }, + { + "name": "staking", + "prefix": "Staking", + "storage": [ + { + "name": "ValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x04000000", + "docs": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x3c000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x40420f00", + "docs": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "Invulnerables", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Any validators that may never be slashed or forcibly kicked. It's a Vec since they're easy to initialise", + " and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets." + ] + }, + { + "name": "Bonded", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountId", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Map from all locked \"stash\" accounts to the controller account." + ] + }, + { + "name": "Ledger", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "StakingLedger", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Map from all (unlocked) \"controller\" accounts to the info regarding the staking." + ] + }, + { + "name": "Payee", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "RewardDestination", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Where the reward payment should be made. Keyed by stash." + ] + }, + { + "name": "Validators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "ValidatorPrefs", + "isLinked": true + } + }, + "fallback": "0x0c00", + "docs": [ + " The map from (wannabe) validator stash key to the preferences of that validator." + ] + }, + { + "name": "Nominators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": true + } + }, + "fallback": "0x00", + "docs": [ + " The map from nominator stash key to the set of stash keys of all validators to nominate." + ] + }, + { + "name": "Stakers", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Exposure", + "isLinked": false + } + }, + "fallback": "0x000000", + "docs": [ + " Nominators for a particular account that is in action right now. You can't iterate through validators here,", + " but you can find them in the `sessions` module.", + "", + " This is keyed by the stash account." + ] + }, + { + "name": "CurrentElected", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The currently elected validator set keyed by stash account ID." + ] + }, + { + "name": "CurrentEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current era index." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentEraReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The accumulated reward for the current era. Reset to zero at the beginning of the era and", + " increased for every successfully finished session." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "SlotStake", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of balance actively at stake for each validator slot, currently.", + "", + " This is used to derive rewards and punishments." + ] + }, + { + "name": "SlashCount", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "u32", + "isLinked": false + } + }, + "fallback": "0x00000000", + "docs": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": "Optional", + "type": { + "PlainType": "Null" + }, + "fallback": "0x00", + "docs": [ + " We are forcing a new era." + ] + }, + { + "name": "RecentlyOffline", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber,u32)>" + }, + "fallback": "0x00", + "docs": [ + " Most recent `RECENT_OFFLINE_COUNT` instances. (who it was, when it was reported, how many instances they were offline for)." + ] + } + ], + "calls": [ + { + "name": "bond", + "args": [ + { + "name": "controller", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " Take the origin account as a stash and lock up `value` of its balance. `controller` will be the", + " account that controls it.", + "", + " The dispatch origin for this call must be _Signed_." + ] + }, + { + "name": "bond_extra", + "args": [ + { + "name": "max_additional", + "type": "BalanceOf" + } + ], + "docs": [ + " Add some extra amount that have appeared in the stash `free_balance` into the balance up for", + " staking.", + "", + " Use this if there are additional funds in your stash account that you wish to bond.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "unbond", + "args": [ + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Schedule a portion of the stash to be unlocked ready for transfer out after the bond", + " period ends. If this leaves an amount actively bonded less than", + " T::Currency::existential_deposit(), then it is increased to the full amount.", + "", + " Once the unlock period is done, you can call `withdraw_unbonded` to actually move", + " the funds out of management ready for transfer.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + " See also [`Call::withdraw_unbonded`]." + ] + }, + { + "name": "withdraw_unbonded", + "args": [], + "docs": [ + " Remove any unlocked chunks from the `unlocking` queue from our management.", + "", + " This essentially frees up that balance to be used by the stash account to do", + " whatever it wants.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + " See also [`Call::unbond`]." + ] + }, + { + "name": "validate", + "args": [ + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "docs": [ + " Declare the desire to validate for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "nominate", + "args": [ + { + "name": "targets", + "type": "Vec
" + } + ], + "docs": [ + " Declare the desire to nominate `targets` for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "chill", + "args": [], + "docs": [ + " Declare no desire to either validate or nominate.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "set_payee", + "args": [ + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "set_controller", + "args": [ + { + "name": "controller", + "type": "Address" + } + ], + "docs": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "set_sessions_per_era", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the number of sessions in an era." + ] + }, + { + "name": "set_bonding_duration", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The length of the bonding duration in eras." + ] + }, + { + "name": "set_validator_count", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The ideal number of validators." + ] + }, + { + "name": "force_new_era", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "name": "set_offline_slash_grace", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the offline slash grace period." + ] + }, + { + "name": "set_invulnerables", + "args": [ + { + "name": "validators", + "type": "Vec" + } + ], + "docs": [ + " Set the validators who cannot be slashed (if any)." + ] + } + ], + "events": [ + { + "name": "Reward", + "args": [ + "Balance" + ], + "docs": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "args": [ + "AccountId", + "u32" + ], + "docs": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + }, + { + "name": "democracy", + "prefix": "Democracy", + "storage": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "PlainType": "PropIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "PlainType": "Vec<(PropIndex,Proposal,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "PropIndex", + "value": "(BalanceOf,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "MaxLockPeriods", + "modifier": "Default", + "type": { + "PlainType": "LockPeriods" + }, + "fallback": "0x00", + "docs": [ + " The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "ReferendumInfo", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Vec>", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "VotersFor", + "modifier": "Default", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "(ReferendumIndex,AccountId)", + "value": "Vote", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the", + " voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check", + " `voters_for`, then you can also check for simple existence with `VoteOf::exists` first." + ] + }, + { + "name": "Delegations", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "(AccountId,LockPeriods)", + "isLinked": true + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Get the account (and lock periods) to which another account is delegating vote." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "second", + "args": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "vote", + "args": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "Vote" + } + ], + "docs": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo." + ] + }, + { + "name": "start_referendum", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Start a referendum." + ] + }, + { + "name": "cancel_referendum", + "args": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a referendum." + ] + }, + { + "name": "cancel_queued", + "args": [ + { + "name": "when", + "type": "Compact" + }, + { + "name": "which", + "type": "Compact" + } + ], + "docs": [ + " Cancel a proposal queued for enactment." + ] + }, + { + "name": "delegate", + "args": [ + { + "name": "to", + "type": "AccountId" + }, + { + "name": "lock_periods", + "type": "LockPeriods" + } + ], + "docs": [ + " Delegate vote." + ] + }, + { + "name": "undelegate", + "args": [], + "docs": [ + " Undelegate vote." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "PropIndex", + "Balance" + ], + "docs": [] + }, + { + "name": "Tabled", + "args": [ + "PropIndex", + "Balance", + "Vec" + ], + "docs": [] + }, + { + "name": "Started", + "args": [ + "ReferendumIndex", + "VoteThreshold" + ], + "docs": [] + }, + { + "name": "Passed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "NotPassed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Cancelled", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Executed", + "args": [ + "ReferendumIndex", + "bool" + ], + "docs": [] + }, + { + "name": "Delegated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [] + }, + { + "name": "Undelegated", + "args": [ + "AccountId" + ], + "docs": [] + } + ] + }, + { + "name": "council", + "prefix": "Council", + "storage": [ + { + "name": "CandidacyBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x09000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x02000000", + "docs": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x01000000", + "docs": [ + " How many vote indexes need to go by after a target voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0500000000000000", + "docs": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber)>" + }, + "fallback": "0x00", + "docs": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters. The block number (second element in the tuple) is the block that their position is", + " active until (calculated by the sum of the block number when the council member was elected", + " and their term duration)." + ] + }, + { + "name": "VoteCount", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A list of votes for each voter, respecting the last cleared vote index that this voter was", + " last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "(VoteIndex,u32)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VoteIndex", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [] + }, + { + "name": "NextFinalise", + "modifier": "Optional", + "type": { + "PlainType": "(BlockNumber,u32,Vec)" + }, + "fallback": "0x00", + "docs": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": "Optional", + "type": { + "PlainType": "Vec<(BalanceOf,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ], + "calls": [ + { + "name": "set_approvals", + "args": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "name": "reap_inactive_voter", + "args": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "name": "retract_voter", + "args": [ + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "name": "submit_candidacy", + "args": [ + { + "name": "slot", + "type": "Compact" + } + ], + "docs": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "name": "present_winner", + "args": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "name": "set_desired_seats", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "name": "remove_member", + "args": [ + { + "name": "who", + "type": "Address" + } + ], + "docs": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "name": "set_presentation_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalise_vote`." + ] + }, + { + "name": "set_term_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalise_vote`." + ] + } + ], + "events": [ + { + "name": "VoterReaped", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "args": [ + "AccountId" + ], + "docs": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "args": [ + "u32" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalised", + "args": [ + "Vec", + "Vec" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + }, + { + "name": "council_voting", + "prefix": "CouncilVoting", + "storage": [ + { + "name": "CooloffPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0300000000000000", + "docs": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec<(BlockNumber,Hash)>" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalVoters", + "modifier": "Default", + "type": { + "MapType": { + "key": "Hash", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "CouncilVoteOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "(Hash,AccountId)", + "value": "bool", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "VetoedProposal", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(BlockNumber,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + }, + { + "name": "veto", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [] + }, + { + "name": "set_cooloff_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + }, + { + "name": "set_voting_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "TallyCancelation", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + }, + { + "name": "council_motions", + "prefix": "CouncilMotions", + "storage": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "docs": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "docs": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "bool" + ], + "docs": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + }, + { + "name": "finality_tracker", + "prefix": "", + "storage": null, + "calls": [ + { + "name": "final_hint", + "args": [ + { + "name": "hint", + "type": "Compact" + } + ], + "docs": [ + " Hint that the author of this block thinks the best finalized", + " block is the given number." + ] + } + ], + "events": null + }, + { + "name": "grandpa", + "prefix": "GrandpaFinality", + "storage": [ + { + "name": "PendingChange", + "modifier": "Optional", + "type": { + "PlainType": "StoredPendingChange" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "NextForced", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehaviour." + ] + } + ], + "events": [ + { + "name": "NewAuthorities", + "args": [ + "Vec<(SessionKey,u64)>" + ], + "docs": [ + " New authority set has been applied." + ] + } + ] + }, + { + "name": "treasury", + "prefix": "Treasury", + "storage": [ + { + "name": "ProposalBond", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0100000000000000", + "docs": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "ProposalIndex" + }, + "fallback": "0x00000000", + "docs": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ProposalIndex", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ], + "calls": [ + { + "name": "propose_spend", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "docs": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "name": "set_pot", + "args": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "docs": [ + " Set the balance of funds available to spend." + ] + }, + { + "name": "configure", + "args": [ + { + "name": "proposal_bond", + "type": "Compact" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Compact" + } + ], + "docs": [ + " (Re-)configure this module." + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "name": "approve_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "ProposalIndex" + ], + "docs": [ + " New proposal." + ] + }, + { + "name": "Spending", + "args": [ + "Balance" + ], + "docs": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "args": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "docs": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "args": [ + "Balance" + ], + "docs": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "args": [ + "Balance" + ], + "docs": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + }, + { + "name": "contract", + "prefix": "Contract", + "storage": [ + { + "name": "ContractFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x15000000000000000000000000000000", + "docs": [ + " The fee required to create a contract. At least as big as staking's ReclaimRebate." + ] + }, + { + "name": "CallBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x8700000000000000", + "docs": [ + " The fee charged for a call into a contract." + ] + }, + { + "name": "CreateBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0xaf00000000000000", + "docs": [ + " The fee charged for a create of a contract." + ] + }, + { + "name": "GasPrice", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The price of one unit of gas." + ] + }, + { + "name": "MaxDepth", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x64000000", + "docs": [ + " The maximum nesting level of a call/create stack." + ] + }, + { + "name": "BlockGasLimit", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x40420f0000000000", + "docs": [ + " The maximum amount of gas that could be expended per block." + ] + }, + { + "name": "GasSpent", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Gas spent so far in this block." + ] + }, + { + "name": "CurrentSchedule", + "modifier": "Default", + "type": { + "PlainType": "Schedule" + }, + "fallback": "0x000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000000010010000000", + "docs": [ + " Current cost schedule for contracts." + ] + }, + { + "name": "CodeHashOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "CodeHash", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The code associated with a given account." + ] + }, + { + "name": "PristineCode", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "Bytes", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A mapping from an original code hash to the original code, untouched by instrumentation." + ] + }, + { + "name": "CodeStorage", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "PrefabWasmModule", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A mapping between an original code hash and instrumented wasm code, ready for the execution." + ] + }, + { + "name": "AccountCounter", + "modifier": "Default", + "type": { + "PlainType": "u64" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The subtrie counter" + ] + }, + { + "name": "AccountInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountInfo", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The code associated with a given account." + ] + } + ], + "calls": [ + { + "name": "update_schedule", + "args": [ + { + "name": "schedule", + "type": "Schedule" + } + ], + "docs": [ + " Updates the schedule for metering contracts.", + "", + " The schedule must have a greater version than the stored schedule." + ] + }, + { + "name": "put_code", + "args": [ + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [ + " Stores code in the storage. You can instantiate contracts only with stored code." + ] + }, + { + "name": "call", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Make a call to a specified account, optionally transferring some balance." + ] + }, + { + "name": "create", + "args": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code_hash", + "type": "CodeHash" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Create a new contract, optionally transferring some balance to the created account.", + "", + " Creation is executed as follows:", + "", + " - the destination address is computed based on the sender and hash of the code.", + " - account is created at the computed address.", + " - the `ctor_code` is executed in the context of the newly created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any message received by this account." + ] + } + ], + "events": [ + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " Transfer happened `from` -> `to` with given `value` as part of a `message-call` or `create`." + ] + }, + { + "name": "Instantiated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " Contract deployed by address at the specified address." + ] + }, + { + "name": "CodeStored", + "args": [ + "Hash" + ], + "docs": [ + " Code with the specified hash has been stored." + ] + }, + { + "name": "ScheduleUpdated", + "args": [ + "u32" + ], + "docs": [ + " Triggered when the current schedule is updated." + ] + }, + { + "name": "Dispatched", + "args": [ + "AccountId", + "bool" + ], + "docs": [ + " A call was dispatched from the given account. The bool signals whether it was", + " successful execution or not." + ] + } + ] + }, + { + "name": "sudo", + "prefix": "Sudo", + "storage": [ + { + "name": "Key", + "modifier": "Default", + "type": { + "PlainType": "AccountId" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + } + ], + "calls": [ + { + "name": "sudo", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "set_key", + "args": [ + { + "name": "new", + "type": "Address" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Sudid", + "args": [ + "bool" + ], + "docs": [ + " A sudo just took place." + ] + }, + { + "name": "KeyChanged", + "args": [ + "AccountId" + ], + "docs": [ + " The sudoer just switched identity; the old key is supplied." + ] + } + ] + } + ] + } + } +} diff --git a/packages/types/src/Metadata/v2/static.ts b/packages/types/src/Metadata/v2/static.ts new file mode 100644 index 0000000000..b03b5fcf09 --- /dev/null +++ b/packages/types/src/Metadata/v2/static.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const meta = '0x6d65746102441873797374656d1853797374656d012c304163636f756e744e6f6e6365010130543a3a4163636f756e74496420543a3a496e64657800200000000000000000003845787472696e736963436f756e7400000c75333204000040416c6c45787472696e736963734c656e00000c75333204000024426c6f636b48617368010138543a3a426c6f636b4e756d6265721c543a3a4861736800800000000000000000000000000000000000000000000000000000000000000000003445787472696e7369634461746101010c7533321c5665633c75383e000400002852616e646f6d5365656401001c543a3a4861736880000000000000000000000000000000000000000000000000000000000000000000184e756d626572010038543a3a426c6f636b4e756d626572200000000000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801001c543a3a48617368800000000000000000000000000000000000000000000000000000000000000000003845787472696e73696373526f6f7401001c543a3a486173688000000000000000000000000000000000000000000000000000000000000000000018446967657374010024543a3a446967657374040000184576656e74730100685665633c4576656e745265636f72643c543a3a4576656e743e3e0400000001084045787472696e7369635375636365737300049420416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656400045420416e2065787472696e736963206661696c65642e1061757261000000002474696d657374616d702454696d657374616d70010c0c4e6f77010024543a3a4d6f6d656e7420000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2c426c6f636b506572696f64010024543a3a4d6f6d656e7420050000000000000004c420546865206d696e696d756d2028616e6420616476697365642920706572696f64206265747765656e20626c6f636b732e24446964557064617465010010626f6f6c040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f01040c736574040c6e6f7748436f6d706163743c543a3a4d6f6d656e743e205820536574207468652063757272656e742074696d652e00750120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e2070686173652cbc20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e008501205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e74207370656369666965642062792060626c6f636b5f706572696f64602e0420d820546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e74602e0024636f6e73656e73757324436f6e73656e73757301044c4f726967696e616c417574686f7269746965730000485665633c543a3a53657373696f6e4b65793e0400000118487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e306e6f74655f6f66666c696e65041c6f66666c696e65f43c543a3a496e686572656e744f66666c696e655265706f727420617320496e686572656e744f66666c696e655265706f72743e3a3a496e686572656e74044501204e6f7465207468652070726576696f757320626c6f636b27732076616c696461746f72206d6973736564207468656972206f70706f7274756e69747920746f2070726f706f7365206120626c6f636b2e1872656d61726b041c5f72656d61726b1c5665633c75383e046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040c6e65771c5665633c75383e04482053657420746865206e657720636f64652e2c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e001c696e64696365731c496e646963657301082c4e657874456e756d53657401003c543a3a4163636f756e74496e6465781000000000047c20546865206e657874206672656520656e756d65726174696f6e207365742e1c456e756d53657401013c543a3a4163636f756e74496e646578445665633c543a3a4163636f756e7449643e00040004582054686520656e756d65726174696f6e20736574732e010001043c4e65774163636f756e74496e64657808244163636f756e744964304163636f756e74496e64657810882041206e6577206163636f756e7420696e646578207761732061737369676e65642e0005012054686973206576656e74206973206e6f7420747269676765726564207768656e20616e206578697374696e6720696e64657820697320726561737369676e65646020746f20616e6f7468657220604163636f756e744964602e2062616c616e6365732042616c616e636573012834546f74616c49737375616e6365010028543a3a42616c616e6365400000000000000000000000000000000004a42054686520746f74616c20616d6f756e74206f66207374616b65206f6e207468652073797374656d2e484578697374656e7469616c4465706f736974010028543a3a42616c616e6365400000000000000000000000000000000004d020546865206d696e696d756d20616d6f756e7420616c6c6f77656420746f206b65657020616e206163636f756e74206f70656e2e2c5472616e73666572466565010028543a3a42616c616e636540000000000000000000000000000000000494205468652066656520726571756972656420746f206d616b652061207472616e736665722e2c4372656174696f6e466565010028543a3a42616c616e63654000000000000000000000000000000000042501205468652066656520726571756972656420746f2063726561746520616e206163636f756e742e204174206c6561737420617320626967206173205265636c61696d5265626174652e485472616e73616374696f6e42617365466565010028543a3a42616c616e6365400000000000000000000000000000000004dc205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b2074686520626173652e485472616e73616374696f6e42797465466565010028543a3a42616c616e63654000000000000000000000000000000000040d01205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b20746865207065722d6279746520706f7274696f6e2e1c56657374696e67000130543a3a4163636f756e7449646c56657374696e675363686564756c653c543a3a42616c616e63653e00040004d820496e666f726d6174696f6e20726567617264696e67207468652076657374696e67206f66206120676976656e206163636f756e742e2c4672656542616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e63650040000000000000000000000000000000002c9c20546865202766726565272062616c616e6365206f66206120676976656e206163636f756e742e004d01205468697320697320746865206f6e6c792062616c616e63652074686174206d61747465727320696e207465726d73206f66206d6f7374206f7065726174696f6e73206f6e20746f6b656e732e204974206973690120616c6f6e65207573656420746f2064657465726d696e65207468652062616c616e6365207768656e20696e2074686520636f6e747261637420657865637574696f6e20656e7669726f6e6d656e742e205768656e207468697355012062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e20746865202763757272656e74206163636f756e74272069733d012064656c657465643a207370656369666963616c6c7920604672656542616c616e6365602e20467572746865726d6f72652c20604f6e4672656542616c616e63655a65726f602063616c6c6261636b410120697320696e766f6b65642c20676976696e672061206368616e636520746f2065787465726e616c206d6f64756c657320746f20636c65616e75702064617461206173736f636961746564207769746854207468652064656c65746564206163636f756e742e005d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c657465642069662060526573657276656442616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e3c526573657276656442616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e63650040000000000000000000000000000000003075012054686520616d6f756e74206f66207468652062616c616e6365206f66206120676976656e206163636f756e7420746861742069732065787465726e616c6c792072657365727665643b20746869732063616e207374696c6c206765749c20736c61736865642c20627574206765747320736c6173686564206c617374206f6620616c6c2e006d0120546869732062616c616e63652069732061202772657365727665272062616c616e63652074686174206f746865722073756273797374656d732075736520696e206f7264657220746f2073657420617369646520746f6b656e737101207468617420617265207374696c6c20276f776e65642720627920746865206163636f756e7420686f6c6465722c20627574207768696368206172652073757370656e6461626c652e20285468697320697320646966666572656e74290120616e642077686f6c6c7920756e72656c6174656420746f207468652060426f6e64616765602073797374656d207573656420696e20746865207374616b696e67206d6f64756c652e29007501205768656e20746869732062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e2074686973202772657365727665206163636f756e7427b42069732064656c657465643a207370656369666963616c6c792c2060526573657276656442616c616e6365602e004d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c6574656420696620604672656542616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e144c6f636b73010130543a3a4163636f756e744964b05665633c42616c616e63654c6f636b3c543a3a42616c616e63652c20543a3a426c6f636b4e756d6265723e3e00040004b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e0108207472616e736665720810646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c75654c436f6d706163743c543a3a42616c616e63653e04d4205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572207374616b65722e2c7365745f62616c616e63650c0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636510667265654c436f6d706163743c543a3a42616c616e63653e2072657365727665644c436f6d706163743c543a3a42616c616e63653e049420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e010c284e65774163636f756e7408244163636f756e7449641c42616c616e6365046c2041206e6577206163636f756e742077617320637265617465642e345265617065644163636f756e7404244163636f756e744964045c20416e206163636f756e7420776173207265617065642e205472616e7366657210244163636f756e744964244163636f756e7449641c42616c616e63651c42616c616e636504b0205472616e7366657220737563636565646564202866726f6d2c20746f2c2076616c75652c2066656573292e1c73657373696f6e1c53657373696f6e01202856616c696461746f72730100445665633c543a3a4163636f756e7449643e0400047c205468652063757272656e7420736574206f662076616c696461746f72732e3453657373696f6e4c656e677468010038543a3a426c6f636b4e756d62657220e803000000000000047c2043757272656e74206c656e677468206f66207468652073657373696f6e2e3043757272656e74496e646578010038543a3a426c6f636b4e756d62657220000000000000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e3043757272656e745374617274010024543a3a4d6f6d656e7420000000000000000004a02054696d657374616d70207768656e2063757272656e742073657373696f6e20737461727465642e44466f7263696e674e657753657373696f6e000010626f6f6c0400087901204e65772073657373696f6e206973206265696e6720666f72636564206973207468697320656e747279206578697374733b20696e20776869636820636173652c2074686520626f6f6c65616e2076616c75652069732077686574686572810120746865206e65772073657373696f6e2073686f756c6420626520636f6e736964657265642061206e6f726d616c20726f746174696f6e202872657761726461626c6529206f7220657863657074696f6e616c2028736c61736861626c65292e404c6173744c656e6774684368616e6765000038543a3a426c6f636b4e756d626572040004c020426c6f636b206174207768696368207468652073657373696f6e206c656e677468206c617374206368616e6765642e284e6578744b6579466f72000130543a3a4163636f756e74496434543a3a53657373696f6e4b6579000400049020546865206e657874206b657920666f72206120676976656e2076616c696461746f722e444e65787453657373696f6e4c656e677468000038543a3a426c6f636b4e756d6265720400046420546865206e6578742073657373696f6e206c656e6774682e010c1c7365745f6b6579040c6b657934543a3a53657373696f6e4b65790861012053657473207468652073657373696f6e206b6579206f6620605f76616c696461746f726020746f20605f6b6579602e205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e657874242073657373696f6e2e287365745f6c656e677468040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e046d01205365742061206e65772073657373696f6e206c656e6774682e20576f6e2774206b69636b20696e20756e74696c20746865206e6578742073657373696f6e206368616e6765202861742063757272656e74206c656e677468292e44666f7263655f6e65775f73657373696f6e04346170706c795f7265776172647310626f6f6c045820466f726365732061206e65772073657373696f6e2e0104284e657753657373696f6e042c426c6f636b4e756d626572085501204e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f742074686520626c6f636b88206e756d626572206173207468652074797065206d6967687420737567676573742e1c7374616b696e671c5374616b696e6701603856616c696461746f72436f756e7401000c753332100000000004a82054686520696465616c206e756d626572206f66207374616b696e67207061727469636970616e74732e544d696e696d756d56616c696461746f72436f756e7401000c7533321004000000044101204d696e696d756d206e756d626572206f66207374616b696e67207061727469636970616e7473206265666f726520656d657267656e637920636f6e646974696f6e732061726520696d706f7365642e3853657373696f6e73506572457261010038543a3a426c6f636b4e756d62657220e80300000000000004a420546865206c656e677468206f662061207374616b696e672065726120696e2073657373696f6e732e3453657373696f6e52657761726401001c50657262696c6c103c000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e304f66666c696e65536c61736801001c50657262696c6c1040420f0004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e444f66666c696e65536c617368477261636501000c7533321000000000043901204e756d626572206f6620696e7374616e636573206f66206f66666c696e65207265706f727473206265666f726520736c617368696e6720626567696e7320666f722076616c696461746f72732e3c426f6e64696e674475726174696f6e010038543a3a426c6f636b4e756d62657220e80300000000000004b820546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20626c6f636b732e34496e76756c6e657261626c65730100445665633c543a3a4163636f756e7449643e040008a50120416e792076616c696461746f72732074686174206d6179206e6576657220626520736c6173686564206f7220666f726369626c79206b69636b65642e20497427732061205665632073696e63652074686579277265206561737920746f20696e697469616c697365ad0120616e642074686520706572666f726d616e636520686974206973206d696e696d616c2028776520657870656374206e6f206d6f7265207468616e20666f757220696e76756c6e657261626c65732920616e64207265737472696374656420746f20746573746e6574732e18426f6e646564000130543a3a4163636f756e74496430543a3a4163636f756e744964000400040101204d61702066726f6d20616c6c206c6f636b65642022737461736822206163636f756e747320746f2074686520636f6e74726f6c6c6572206163636f756e742e184c6564676572000130543a3a4163636f756e744964e45374616b696e674c65646765723c543a3a4163636f756e7449642c2042616c616e63654f663c543e2c20543a3a426c6f636b4e756d6265723e000400044501204d61702066726f6d20616c6c2028756e6c6f636b6564292022636f6e74726f6c6c657222206163636f756e747320746f2074686520696e666f20726567617264696e6720746865207374616b696e672e145061796565010130543a3a4163636f756e7449644452657761726444657374696e6174696f6e00040004e42057686572652074686520726577617264207061796d656e742073686f756c64206265206d6164652e204b657965642062792073746173682e2856616c696461746f7273010130543a3a4163636f756e7449647056616c696461746f7250726566733c42616c616e63654f663c543e3e01080c0004450120546865206d61702066726f6d202877616e6e616265292076616c696461746f72207374617368206b657920746f2074686520707265666572656e636573206f6620746861742076616c696461746f722e284e6f6d696e61746f7273010130543a3a4163636f756e744964445665633c543a3a4163636f756e7449643e01040004650120546865206d61702066726f6d206e6f6d696e61746f72207374617368206b657920746f2074686520736574206f66207374617368206b657973206f6620616c6c2076616c696461746f727320746f206e6f6d696e6174652e1c5374616b657273010130543a3a4163636f756e744964904578706f737572653c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e000c00000010b101204e6f6d696e61746f727320666f72206120706172746963756c6172206163636f756e74207468617420697320696e20616374696f6e207269676874206e6f772e20596f752063616e27742069746572617465207468726f7567682076616c696461746f727320686572652cc02062757420796f752063616e2066696e64207468656d20696e20746865206073657373696f6e7360206d6f64756c652e00902054686973206973206b6579656420627920746865207374617368206163636f756e742e3843757272656e74456c65637465640100445665633c543a3a4163636f756e7449643e040004fc205468652063757272656e746c7920656c65637465642076616c696461746f7220736574206b65796564206279207374617368206163636f756e742049442e2843757272656e74457261010038543a3a426c6f636b4e756d626572200000000000000000045c205468652063757272656e742065726120696e6465782e5043757272656e7453657373696f6e52657761726401003042616c616e63654f663c543e4000000000000000000000000000000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e4043757272656e7445726152657761726401003042616c616e63654f663c543e40000000000000000000000000000000000869012054686520616363756d756c617465642072657761726420666f72207468652063757272656e74206572612e20526573657420746f207a65726f2061742074686520626567696e6e696e67206f66207468652065726120616e64cc20696e6372656173656420666f72206576657279207375636365737366756c6c792066696e69736865642073657373696f6e2e484e65787453657373696f6e73506572457261000038543a3a426c6f636b4e756d6265720400049020546865206e6578742076616c7565206f662073657373696f6e7320706572206572612e4c4c6173744572614c656e6774684368616e6765010038543a3a426c6f636b4e756d62657220000000000000000004e0205468652073657373696f6e20696e6465782061742077686963682074686520657261206c656e677468206c617374206368616e6765642e24536c6f745374616b6501003042616c616e63654f663c543e40000000000000000000000000000000000c31012054686520616d6f756e74206f662062616c616e6365206163746976656c79206174207374616b6520666f7220656163682076616c696461746f7220736c6f742c2063757272656e746c792e00c02054686973206973207573656420746f20646572697665207265776172647320616e642070756e6973686d656e74732e28536c617368436f756e74010130543a3a4163636f756e7449640c75333200100000000004d10120546865206e756d626572206f662074696d6573206120676976656e2076616c696461746f7220686173206265656e207265706f72746564206f66666c696e652e205468697320676574732064656372656d656e746564206279206f6e652065616368206572612074686174207061737365732e34466f7263696e674e65774572610000082829040004682057652061726520666f7263696e672061206e6577206572612e3c526563656e746c794f66666c696e650100a05665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d6265722c20753332293e040004f101204d6f737420726563656e742060524543454e545f4f46464c494e455f434f554e546020696e7374616e6365732e202877686f206974207761732c207768656e20697420776173207265706f727465642c20686f77206d616e7920696e7374616e63657320746865792077657265206f66666c696e6520666f72292e013c10626f6e640c28636f6e74726f6c6c65728c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c756554436f6d706163743c42616c616e63654f663c543e3e1470617965654452657761726444657374696e6174696f6e1081012054616b6520746865206f726967696e206163636f756e74206173206120737461736820616e64206c6f636b207570206076616c756560206f66206974732062616c616e63652e2060636f6e74726f6c6c6572602077696c6c2062652074686568206163636f756e74207468617420636f6e74726f6c732069742e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e28626f6e645f657874726104386d61785f6164646974696f6e616c3042616c616e63654f663c543e1875012041646420736f6d6520657874726120616d6f756e742074686174206861766520617070656172656420696e207468652073746173682060667265655f62616c616e63656020696e746f207468652062616c616e636520757020666f7224207374616b696e672e00510120557365207468697320696620746865726520617265206164646974696f6e616c2066756e647320696e20796f7572207374617368206163636f756e74207468617420796f75207769736820746f20626f6e642e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e18756e626f6e64041476616c756554436f6d706163743c42616c616e63654f663c543e3e285501205363686564756c65206120706f7274696f6e206f662074686520737461736820746f20626520756e6c6f636b656420726561647920666f72207472616e73666572206f75742061667465722074686520626f6e64010120706572696f6420656e64732e2049662074686973206c656176657320616e20616d6f756e74206163746976656c7920626f6e646564206c657373207468616e350120543a3a43757272656e63793a3a6578697374656e7469616c5f6465706f73697428292c207468656e20697420697320696e6372656173656420746f207468652066756c6c20616d6f756e742e004901204f6e63652074686520756e6c6f636b20706572696f6420697320646f6e652c20796f752063616e2063616c6c206077697468647261775f756e626f6e6465646020746f2061637475616c6c79206d6f7665c0207468652066756e6473206f7574206f66206d616e6167656d656e7420726561647920666f72207472616e736665722e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e00982053656520616c736f205b6043616c6c3a3a77697468647261775f756e626f6e646564605d2e4477697468647261775f756e626f6e64656400202d012052656d6f766520616e7920756e6c6f636b6564206368756e6b732066726f6d207468652060756e6c6f636b696e67602071756575652066726f6d206f7572206d616e6167656d656e742e003501205468697320657373656e7469616c6c7920667265657320757020746861742062616c616e636520746f206265207573656420627920746865207374617368206163636f756e7420746f20646f4c2077686174657665722069742077616e74732e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e006c2053656520616c736f205b6043616c6c3a3a756e626f6e64605d2e2076616c6964617465041470726566737056616c696461746f7250726566733c42616c616e63654f663c543e3e14e8204465636c617265207468652064657369726520746f2076616c696461746520666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e206e6f6d696e617465041c74617267657473a05665633c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263653e141101204465636c617265207468652064657369726520746f206e6f6d696e6174652060746172676574736020666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e146368696c6c0014c8204465636c617265206e6f2064657369726520746f206569746865722076616c6964617465206f72206e6f6d696e6174652e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e247365745f7061796565041470617965654452657761726444657374696e6174696f6e14b8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e387365745f636f6e74726f6c6c65720428636f6e74726f6c6c65728c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514b8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e507365745f73657373696f6e735f7065725f657261040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04982053657420746865206e756d626572206f662073657373696f6e7320696e20616e206572612e507365745f626f6e64696e675f6475726174696f6e040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04b020546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20657261732e4c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e34666f7263655f6e65775f65726104346170706c795f7265776172647310626f6f6c083d0120466f72636520746865726520746f2062652061206e6577206572612e205468697320616c736f20666f726365732061206e65772073657373696f6e20696d6d6564696174656c792061667465722e250120606170706c795f72657761726473602073686f756c64206265207472756520666f722076616c696461746f727320746f20676574207468652073657373696f6e207265776172642e5c7365745f6f66666c696e655f736c6173685f6772616365040c6e657730436f6d706163743c7533323e04902053657420746865206f66666c696e6520736c61736820677261636520706572696f642e447365745f696e76756c6e657261626c6573042876616c696461746f7273445665633c543a3a4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e010c18526577617264041c42616c616e636504e020416c6c2076616c696461746f72732068617665206265656e2072657761726465642062792074686520676976656e2062616c616e63652e384f66666c696e655761726e696e6708244163636f756e7449640c753332085501204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20676976656e2061206f66666c696e652d7761726e696e67202874686579277265207374696c6c15012077697468696e207468656972206772616365292e205468652061636372756564206e756d626572206f6620736c6173686573206973207265636f726465642c20746f6f2e304f66666c696e65536c61736808244163636f756e7449641c42616c616e6365042d01204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20736c61736865642062792074686520676976656e20616d6f756e742e2464656d6f63726163792444656d6f6372616379013c3c5075626c696350726f70436f756e7401002450726f70496e646578100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100ac5665633c2850726f70496e6465782c20543a3a50726f706f73616c2c20543a3a4163636f756e744964293e0400048020546865207075626c69632070726f706f73616c732e20556e736f727465642e244465706f7369744f6600012450726f70496e646578842842616c616e63654f663c543e2c205665633c543a3a4163636f756e7449643e2900040004842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e304c61756e6368506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e384d696e696d756d4465706f73697401003042616c616e63654f663c543e400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e2c5075626c696344656c6179010038543a3a426c6f636b4e756d62657220000000000000000004d4205468652064656c6179206265666f726520656e6163746d656e7420666f7220616c6c207075626c6963207265666572656e64612e384d61784c6f636b506572696f647301002c4c6f636b506572696f6473040004d90120546865206d6178696d756d206e756d626572206f66206164646974696f6e616c206c6f636b20706572696f6473206120766f746572206d6179206f6666657220746f20737472656e677468656e20746865697220766f74652e204d756c7469706c6573206f6620605075626c696344656c6179602e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e3c5265666572656e64756d436f756e7401003c5265666572656e64756d496e646578100000000004390120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e64756d73207374617274656420736f206661722e244e65787454616c6c7901003c5265666572656e64756d496e646578100000000004c820546865206e657874207265666572656e64756d20696e64657820746861742073686f756c642062652074616c6c6965642e405265666572656e64756d496e666f4f6600013c5265666572656e64756d496e646578b4285265666572656e64756d496e666f3c543a3a426c6f636b4e756d6265722c20543a3a50726f706f73616c3e2900040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e3444697370617463685175657565010138543a3a426c6f636b4e756d626572ac5665633c4f7074696f6e3c28543a3a50726f706f73616c2c205265666572656e64756d496e646578293e3e00040004c0205175657565206f66207375636365737366756c207265666572656e646120746f20626520646973706174636865642e24566f74657273466f7201013c5265666572656e64756d496e646578445665633c543a3a4163636f756e7449643e00040004a4204765742074686520766f7465727320666f72207468652063757272656e742070726f706f73616c2e18566f74654f6601017c285265666572656e64756d496e6465782c20543a3a4163636f756e7449642910566f74650004000cd501204765742074686520766f746520696e206120676976656e207265666572656e64756d206f66206120706172746963756c617220766f7465722e2054686520726573756c74206973206d65616e696e6766756c206f6e6c792069662060766f746572735f666f726020696e636c7564657320746865c90120766f746572207768656e2063616c6c6564207769746820746865207265666572656e64756d2028796f75276c6c20676574207468652064656661756c742060566f7465602076616c7565206f7468657277697365292e20496620796f7520646f6e27742077616e7420746f20636865636b61012060766f746572735f666f72602c207468656e20796f752063616e20616c736f20636865636b20666f722073696d706c65206578697374656e636520776974682060566f74654f663a3a657869737473602066697273742e2c44656c65676174696f6e73010130543a3a4163636f756e7449646c28543a3a4163636f756e7449642c204c6f636b506572696f64732901840000000000000000000000000000000000000000000000000000000000000000000441012047657420746865206163636f756e742028616e64206c6f636b20706572696f64732920746f20776869636820616e6f74686572206163636f756e742069732064656c65676174696e6720766f74652e01201c70726f706f7365082070726f706f73616c40426f783c543a3a50726f706f73616c3e1476616c756554436f6d706163743c42616c616e63654f663c543e3e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e10766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f746508350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e4073746172745f7265666572656e64756d0c2070726f706f73616c40426f783c543a3a50726f706f73616c3e247468726573686f6c6434566f74655468726573686f6c641464656c617938543a3a426c6f636b4e756d62657204502053746172742061207265666572656e64756d2e4463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e3463616e63656c5f71756575656408107768656e5c436f6d706163743c543a3a426c6f636b4e756d6265723e14776869636830436f6d706163743c7533323e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e2064656c65676174650808746f30543a3a4163636f756e744964306c6f636b5f706572696f64732c4c6f636b506572696f6473043c2044656c656761746520766f74652e28756e64656c656761746500044420556e64656c656761746520766f74652e01242050726f706f736564082450726f70496e6465781c42616c616e636500185461626c65640c2450726f70496e6465781c42616c616e6365385665633c4163636f756e7449643e001c53746172746564083c5265666572656e64756d496e64657834566f74655468726573686f6c640018506173736564043c5265666572656e64756d496e64657800244e6f74506173736564043c5265666572656e64756d496e646578002443616e63656c6c6564043c5265666572656e64756d496e64657800204578656375746564083c5265666572656e64756d496e64657810626f6f6c002444656c65676174656408244163636f756e744964244163636f756e744964002c556e64656c65676174656404244163636f756e744964001c636f756e63696c1c436f756e63696c01503443616e646964616379426f6e6401003042616c616e63654f663c543e400900000000000000000000000000000004050120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f207375626d6974206f6e6527732063616e6469646163792e28566f74696e67426f6e6401003042616c616e63654f663c543e400000000000000000000000000000000004090120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f2062652061626c6520746f207375626d697420766f7465732e5050726573656e74536c617368506572566f74657201003042616c616e63654f663c543e4001000000000000000000000000000000040d01205468652070756e6973686d656e742c2070657220766f7465722c20696620796f752070726f7669646520616e20696e76616c69642070726573656e746174696f6e2e284361727279436f756e7401000c753332100200000004350120486f77206d616e792072756e6e6572732d75702073686f756c64206861766520746865697220617070726f76616c73207065727369737420756e74696c20746865206e65787420766f74652e5050726573656e746174696f6e4475726174696f6e010038543a3a426c6f636b4e756d62657220e803000000000000043d0120486f77206c6f6e6720746f2067697665206561636820746f702063616e64696461746520746f2070726573656e74207468656d73656c7665732061667465722074686520766f746520656e64732e4c496e6163746976654772616365506572696f64010024566f7465496e646578100100000008a10120486f77206d616e7920766f746520696e6465786573206e65656420746f20676f20627920616674657220612074617267657420766f7465722773206c61737420766f7465206265666f726520746865792063616e206265207265617065642069662074686569725020617070726f76616c7320617265206d6f6f742e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e305465726d4475726174696f6e010038543a3a426c6f636b4e756d626572200500000000000000049820486f77206c6f6e67206561636820706f736974696f6e2069732061637469766520666f722e3044657369726564536561747301000c753332100000000004e8204e756d626572206f66206163636f756e747320746861742073686f756c642062652073697474696e67206f6e2074686520636f756e63696c2e34416374697665436f756e63696c01008c5665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d626572293e0400106d01205468652063757272656e7420636f756e63696c2e205768656e2074686572652773206120766f746520676f696e67206f6e2c20746869732073686f756c64207374696c6c206265207573656420666f72206578656375746976657101206d6174746572732e2054686520626c6f636b206e756d62657220287365636f6e6420656c656d656e7420696e20746865207475706c65292069732074686520626c6f636b207468617420746865697220706f736974696f6e20697371012061637469766520756e74696c202863616c63756c61746564206279207468652073756d206f662074686520626c6f636b206e756d626572207768656e2074686520636f756e63696c206d656d6265722077617320656c65637465646820616e64207468656972207465726d206475726174696f6e292e24566f7465436f756e74010024566f7465496e64657810000000000405012054686520746f74616c206e756d626572206f6620766f746573207468617420686176652068617070656e6564206f722061726520696e2070726f67726573732e2c417070726f76616c734f66010130543a3a4163636f756e744964245665633c626f6f6c3e000400086d012041206c697374206f6620766f74657320666f72206561636820766f7465722c2072657370656374696e6720746865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f7465722077617340206c617374206163746976652061742e385265676973746572496e666f4f66000130543a3a4163636f756e7449644028566f7465496e6465782c2075333229000400087d012054686520766f746520696e64657820616e64206c69737420736c6f742074686174207468652063616e646964617465206077686f60207761732072656769737465726564206f7220604e6f6e6560206966207468657920617265206e6f74582063757272656e746c7920726567697374657265642e304c6173744163746976654f66000130543a3a4163636f756e74496424566f7465496e64657800040004010120546865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f74657220776173206c617374206163746976652061742e18566f746572730100445665633c543a3a4163636f756e7449643e04000460205468652070726573656e7420766f746572206c6973742e2843616e646964617465730100445665633c543a3a4163636f756e7449643e04000470205468652070726573656e742063616e646964617465206c6973742e3843616e646964617465436f756e7401000c753332100000000000304e65787446696e616c6973650000a028543a3a426c6f636b4e756d6265722c207533322c205665633c543a3a4163636f756e7449643e29040004210120546865206163636f756e747320686f6c64696e672074686520736561747320746861742077696c6c206265636f6d652066726565206f6e20746865206e6578742074616c6c792e40536e617073686f7465645374616b65730100445665633c42616c616e63654f663c543e3e040004e820546865207374616b6573206173207468657920776572652061742074686520706f696e7420746861742074686520766f746520656e6465642e2c4c6561646572626f6172640000845665633c2842616c616e63654f663c543e2c20543a3a4163636f756e744964293e040004e02047657420746865206c6561646572626f6172642069662077653b726520696e207468652070726573656e746174696f6e2070686173652e0124347365745f617070726f76616c730814766f746573245665633c626f6f6c3e14696e64657848436f6d706163743c566f7465496e6465783e086101205365742063616e64696461746520617070726f76616c732e20417070726f76616c20736c6f747320737461792076616c6964206173206c6f6e672061732063616e6469646174657320696e2074686f736520736c6f7473402061726520726567697374657265642e4c726561705f696e6163746976655f766f74657210387265706f727465725f696e64657830436f6d706163743c7533323e0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263652477686f5f696e64657830436f6d706163743c7533323e48617373756d65645f766f74655f696e64657848436f6d706163743c566f7465496e6465783e1461012052656d6f7665206120766f7465722e20466f72206974206e6f7420746f206265206120626f6e642d636f6e73756d696e67206e6f2d6f702c20616c6c20617070726f7665642063616e64696461746520696e64696365737101206d757374206e6f772062652065697468657220756e72656769737465726564206f72207265676973746572656420746f20612063616e646964617465207468617420726567697374657265642074686520736c6f74206166746572a02074686520766f7465722067617665207468656972206c61737420617070726f76616c207365742e000101204d61792062652063616c6c656420627920616e796f6e652e2052657475726e732074686520766f746572206465706f73697420746f20607369676e6564602e34726574726163745f766f7465720414696e64657830436f6d706163743c7533323e042d012052656d6f7665206120766f7465722e20416c6c20766f746573206172652063616e63656c6c656420616e642074686520766f746572206465706f7369742069732072657475726e65642e407375626d69745f63616e6469646163790410736c6f7430436f6d706163743c7533323e0c78205375626d6974206f6e6573656c6620666f722063616e6469646163792e001101204163636f756e74206d757374206861766520656e6f756768207472616e736665727261626c652066756e647320696e20697420746f207061792074686520626f6e642e3870726573656e745f77696e6e65720c2463616e6469646174658c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514746f74616c54436f6d706163743c42616c616e63654f663c543e3e14696e64657848436f6d706163743c566f7465496e6465783e0c650120436c61696d207468617420607369676e656460206973206f6e65206f662074686520746f702053656c663a3a63617272795f636f756e742829202b2063757272656e745f766f746528292e312063616e646964617465732ea501204f6e6c7920776f726b73206966207468652060626c6f636b5f6e756d626572203e3d2063757272656e745f766f746528292e306020616e6420603c2063757272656e745f766f746528292e30202b2070726573656e746174696f6e5f6475726174696f6e282960607820607369676e6564602073686f756c642068617665206174206c65617374447365745f646573697265645f73656174730414636f756e7430436f6d706163743c7533323e0c650120536574207468652064657369726564206d656d62657220636f756e743b206966206c6f776572207468616e207468652063757272656e7420636f756e742c207468656e2073656174732077696c6c206e6f74206265207570690120656c656374696f6e207768656e2074686579206578706972652e204966206d6f72652c207468656e2061206e657720766f74652077696c6c2062652073746172746564206966206f6e65206973206e6f7420616c72656164793420696e2070726f67726573732e3472656d6f76655f6d656d626572040c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c71012052656d6f7665206120706172746963756c6172206d656d6265722e20412074616c6c792077696c6c2068617070656e20696e7374616e746c7920286966206e6f7420616c726561647920696e20612070726573656e746174696f6e410120706572696f642920746f2066696c6c2074686520736561742069662072656d6f76616c206d65616e732074686174207468652064657369726564206d656d6265727320617265206e6f74206d65742e7c20546869732069732065666665637469766520696d6d6564696174656c792e647365745f70726573656e746174696f6e5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08590120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e746c79206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e447365745f7465726d5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08510120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e74206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c6973655f766f7465602e01102c566f74657252656170656408244163636f756e744964244163636f756e74496404542072656170656420766f7465722c2072656170657240426164526561706572536c617368656404244163636f756e744964043c20736c6173686564207265617065723054616c6c7953746172746564040c75333204f420412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320737461727465642e3854616c6c7946696e616c6973656408385665633c4163636f756e7449643e385665633c4163636f756e7449643e04690120412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320656e646564202877697468206f6e65206f72206d6f7265206e6577206d656d62657273292e38636f756e63696c5f766f74696e6734436f756e63696c566f74696e67012034436f6f6c6f6666506572696f64010038543a3a426c6f636b4e756d62657220e8030000000000000030566f74696e67506572696f64010038543a3a426c6f636b4e756d6265722003000000000000000040456e61637444656c6179506572696f64010038543a3a426c6f636b4e756d62657220000000000000000004cd01204e756d626572206f6620626c6f636b7320627920776869636820746f2064656c617920656e6163746d656e74206f66207375636365737366756c2c206e6f6e2d756e616e696d6f75732d636f756e63696c2d696e7374696761746564207265666572656e64756d2070726f706f73616c732e2450726f706f73616c730100785665633c28543a3a426c6f636b4e756d6265722c20543a3a48617368293e0400002850726f706f73616c4f6600011c543a3a486173682c543a3a50726f706f73616c000400003850726f706f73616c566f7465727301011c543a3a48617368445665633c543a3a4163636f756e7449643e0004000034436f756e63696c566f74654f6600015c28543a3a486173682c20543a3a4163636f756e7449642910626f6f6c00040000385665746f656450726f706f73616c00011c543a3a486173688c28543a3a426c6f636b4e756d6265722c205665633c543a3a4163636f756e7449643e290004000001141c70726f706f7365042070726f706f73616c40426f783c543a3a50726f706f73616c3e0010766f7465082070726f706f73616c1c543a3a486173681c617070726f766510626f6f6c00107665746f043470726f706f73616c5f686173681c543a3a4861736800487365745f636f6f6c6f66665f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e00447365745f766f74696e675f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e0001084054616c6c7943616e63656c6174696f6e1010486173680c7533320c7533320c753332080101204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d2063616e63656c6c6174696f6e20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c54616c6c795265666572656e64756d1010486173680c7533320c7533320c75333208cc204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c636f756e63696c5f6d6f74696f6e7338436f756e63696c4d6f74696f6e7301102450726f706f73616c730100305665633c543a3a486173683e04000498205468652028686173686573206f662920746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600011c543a3a48617368583c542061732054726169743e3a3a50726f706f73616c00040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700011c543a3a48617368e82850726f706f73616c496e6465782c207533322c205665633c543a3a4163636f756e7449643e2c205665633c543a3a4163636f756e7449643e2900040004250120566f74657320666f72206120676976656e2070726f706f73616c3a202872657175697265645f7965735f766f7465732c207965735f766f746572732c206e6f5f766f74657273292e3450726f706f73616c436f756e7401000c753332100000000004482050726f706f73616c7320736f206661722e01081c70726f706f736508247468726573686f6c6430436f6d706163743c7533323e2070726f706f73616c6c426f783c3c542061732054726169743e3a3a50726f706f73616c3e0010766f74650c2070726f706f73616c1c543a3a4861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c0001142050726f706f73656410244163636f756e7449643450726f706f73616c496e64657810486173680c7533320465012041206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e20753332292e14566f74656414244163636f756e744964104861736810626f6f6c0c7533320c7533320809012041206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e67f420612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e206173207533327320726573706563746976656c79292e20417070726f76656404104861736804c42041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404104861736804d42041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408104861736810626f6f6c0405012041206d6f74696f6e207761732065786563757465643b2060626f6f6c6020697320747275652069662072657475726e656420776974686f7574206572726f722e4066696e616c6974795f747261636b6572000001042866696e616c5f68696e74041068696e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08f42048696e7420746861742074686520617574686f72206f66207468697320626c6f636b207468696e6b732074686520626573742066696e616c697a65646c20626c6f636b2069732074686520676976656e206e756d6265722e001c6772616e6470613c4772616e64706146696e616c69747901083450656e64696e674368616e67650000c853746f72656450656e64696e674368616e67653c543a3a426c6f636b4e756d6265722c20543a3a53657373696f6e4b65793e040000284e657874466f72636564000038543a3a426c6f636b4e756d6265720400000104487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0468205265706f727420736f6d65206d69736265686176696f75722e0104384e6577417574686f72697469657304585665633c2853657373696f6e4b65792c20753634293e0490204e657720617574686f726974792073657420686173206265656e206170706c6965642e20747265617375727920547265617375727901203050726f706f73616c426f6e6401001c5065726d696c6c10000000000851012050726f706f7274696f6e206f662066756e647320746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c61636520612070726f706f73616c2e20416e206163636570746564dc2070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f65736e27742e4c50726f706f73616c426f6e644d696e696d756d01003042616c616e63654f663c543e4000000000000000000000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64010038543a3a426c6f636b4e756d626572200100000000000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e01001c5065726d696c6c10000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e0c506f7401003042616c616e63654f663c543e400000000000000000000000000000000004cc20546f74616c2066756e647320617661696c61626c6520746f2074686973206d6f64756c6520666f72207370656e64696e672e3450726f706f73616c436f756e7401003450726f706f73616c496e646578100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300013450726f706f73616c496e6465789050726f706f73616c3c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e000400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e24417070726f76616c730100485665633c50726f706f73616c496e6465783e040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e01143470726f706f73655f7370656e64081476616c756554436f6d706163743c42616c616e63654f663c543e3e2c62656e65666963696172798c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c2d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e1c7365745f706f74041c6e65775f706f7454436f6d706163743c42616c616e63654f663c543e3e04b420536574207468652062616c616e6365206f662066756e647320617661696c61626c6520746f207370656e642e24636f6e666967757265103470726f706f73616c5f626f6e6440436f6d706163743c5065726d696c6c3e5470726f706f73616c5f626f6e645f6d696e696d756d54436f6d706163743c42616c616e63654f663c543e3e307370656e645f706572696f645c436f6d706163743c543a3a426c6f636b4e756d6265723e106275726e40436f6d706163743c5065726d696c6c3e0470202852652d29636f6e6669677572652074686973206d6f64756c652e3c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e04fc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e40617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e085d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e01142050726f706f736564043450726f706f73616c496e6465780438204e65772070726f706f73616c2e205370656e64696e67041c42616c616e636504e8205765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c3450726f706f73616c496e6465781c42616c616e6365244163636f756e744964048020536f6d652066756e64732068617665206265656e20616c6c6f63617465642e144275726e74041c42616c616e6365048c20536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572041c42616c616e6365043101205370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e20636f6e747261637420436f6e747261637401342c436f6e7472616374466565010028543a3a42616c616e63654015000000000000000000000000000000044d01205468652066656520726571756972656420746f20637265617465206120636f6e74726163742e204174206c6561737420617320626967206173207374616b696e672773205265636c61696d5265626174652e2c43616c6c42617365466565010018543a3a47617320870000000000000004b02054686520666565206368617267656420666f7220612063616c6c20696e746f206120636f6e74726163742e3443726561746542617365466565010018543a3a47617320af0000000000000004b02054686520666565206368617267656420666f72206120637265617465206f66206120636f6e74726163742e204761735072696365010028543a3a42616c616e63654001000000000000000000000000000000047820546865207072696365206f66206f6e6520756e6974206f66206761732e204d6178446570746801000c753332106400000004c820546865206d6178696d756d206e657374696e67206c6576656c206f6620612063616c6c2f63726561746520737461636b2e34426c6f636b4761734c696d6974010018543a3a4761732040420f000000000004f020546865206d6178696d756d20616d6f756e74206f6620676173207468617420636f756c6420626520657870656e6465642070657220626c6f636b2e204761735370656e74010018543a3a476173200000000000000000048020476173207370656e7420736f2066617220696e207468697320626c6f636b2e3c43757272656e745363686564756c650100405363686564756c653c543a3a4761733ef000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000000001001000000004942043757272656e7420636f7374207363686564756c6520666f7220636f6e7472616374732e28436f6465486173684f66000130543a3a4163636f756e7449642c436f6465486173683c543e00040004a82054686520636f6465206173736f6369617465642077697468206120676976656e206163636f756e742e305072697374696e65436f646500012c436f6465486173683c543e1c5665633c75383e0004000465012041206d617070696e672066726f6d20616e206f726967696e616c20636f6465206861736820746f20746865206f726967696e616c20636f64652c20756e746f756368656420627920696e737472756d656e746174696f6e2e2c436f646553746f7261676500012c436f6465486173683c543e587761736d3a3a5072656661625761736d4d6f64756c650004000475012041206d617070696e67206265747765656e20616e206f726967696e616c20636f6465206861736820616e6420696e737472756d656e746564207761736d20636f64652c20726561647920666f722074686520657865637574696f6e2e384163636f756e74436f756e74657201000c753634200000000000000000045020546865207375627472696520636f756e746572344163636f756e74496e666f4f66000130543a3a4163636f756e7449642c4163636f756e74496e666f00040004a82054686520636f6465206173736f6369617465642077697468206120676976656e206163636f756e742e01103c7570646174655f7363686564756c6504207363686564756c65405363686564756c653c543a3a4761733e0cb4205570646174657320746865207363686564756c6520666f72206d65746572696e6720636f6e7472616374732e000d0120546865207363686564756c65206d7573742068617665206120677265617465722076657273696f6e207468616e207468652073746f726564207363686564756c652e207075745f636f646508246761735f6c696d69743c436f6d706163743c543a3a4761733e10636f64651c5665633c75383e0445012053746f72657320636f646520696e207468652073746f726167652e20596f752063616e20696e7374616e746961746520636f6e747261637473206f6e6c7920776974682073746f72656420636f64652e1063616c6c1010646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c75654c436f6d706163743c543a3a42616c616e63653e246761735f6c696d69743c436f6d706163743c543a3a4761733e10646174611c5665633c75383e042901204d616b6520612063616c6c20746f206120737065636966696564206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e186372656174651024656e646f776d656e744c436f6d706163743c543a3a42616c616e63653e246761735f6c696d69743c436f6d706163743c543a3a4761733e24636f64655f686173682c436f6465486173683c543e10646174611c5665633c75383e245101204372656174652061206e657720636f6e74726163742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e636520746f207468652063726561746564206163636f756e742e0084204372656174696f6e20697320657865637574656420617320666f6c6c6f77733a004101202d207468652064657374696e6174696f6e206164647265737320697320636f6d7075746564206261736564206f6e207468652073656e64657220616e642068617368206f662074686520636f64652eb8202d206163636f756e7420697320637265617465642061742074686520636f6d707574656420616464726573732e6d01202d20746865206063746f725f636f64656020697320657865637574656420696e2074686520636f6e74657874206f6620746865206e65776c792063726561746564206163636f756e742e204275666665722072657475726e65645d0120202061667465722074686520657865637574696f6e206973207361766564206173207468652060636f646560206f6620746865206163636f756e742e205468617420636f64652077696c6c20626520696e766f6b6564b420202075706f6e20616e79206d6573736167652072656365697665642062792074686973206163636f756e742e0114205472616e736665720c244163636f756e744964244163636f756e7449641c42616c616e6365047501205472616e736665722068617070656e6564206066726f6d60202d3e2060746f60207769746820676976656e206076616c7565602061732070617274206f66206120606d6573736167652d63616c6c60206f722060637265617465602e30496e7374616e74696174656408244163636f756e744964244163636f756e74496404dc20436f6e7472616374206465706c6f7965642062792061646472657373206174207468652073706563696669656420616464726573732e28436f646553746f72656404104861736804b820436f646520776974682074686520737065636966696564206861736820686173206265656e2073746f7265642e3c5363686564756c6555706461746564040c75333204c020547269676765726564207768656e207468652063757272656e74207363686564756c6520697320757064617465642e284469737061746368656408244163636f756e74496410626f6f6c08390120412063616c6c2077617320646973706174636865642066726f6d2074686520676976656e206163636f756e742e2054686520626f6f6c207369676e616c7320776865746865722069742077617374207375636365737366756c20657865637574696f6e206f72206e6f742e107375646f105375646f01040c4b6579010030543a3a4163636f756e744964800000000000000000000000000000000000000000000000000000000000000000000108107375646f042070726f706f73616c40426f783c543a3a50726f706f73616c3e001c7365745f6b6579040c6e65778c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001081453756469640410626f6f6c04602041207375646f206a75737420746f6f6b20706c6163652e284b65794368616e67656404244163636f756e74496404f020546865207375646f6572206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c6965642e'; + +export default meta; diff --git a/packages/types/src/Metadata/v2/toV1.ts b/packages/types/src/Metadata/v2/toV1.ts new file mode 100644 index 0000000000..05be1a722c --- /dev/null +++ b/packages/types/src/Metadata/v2/toV1.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import MetadataV1 from '../v1/Metadata'; +import MetadataV2 from '.'; + +export default function toV1 (v2: MetadataV2): MetadataV1 { + return new MetadataV1(v2); +} diff --git a/packages/types/src/Metadata/v3/Metadata.spec.ts b/packages/types/src/Metadata/v3/Metadata.spec.ts new file mode 100644 index 0000000000..8d59de0f61 --- /dev/null +++ b/packages/types/src/Metadata/v3/Metadata.spec.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Metadata from '../Metadata'; +import latestParsed from './latest.substrate.v3.json'; +import rpcData from './static'; + +describe('MetadataV3', () => { + const metadata = new Metadata(rpcData); + + it('decodes latest properly', () => { + const str = JSON.stringify(metadata.toJSON()); + + console.error(str); + console.error(metadata.getUniqTypes(true)); + + expect(metadata.version).toBe(3); + expect(metadata.asV3.modules.length).not.toBe(0); + expect(str).toEqual(JSON.stringify(latestParsed)); + }); +}); diff --git a/packages/types/src/Metadata/v3/Metadata.ts b/packages/types/src/Metadata/v3/Metadata.ts new file mode 100644 index 0000000000..1c53cb3c23 --- /dev/null +++ b/packages/types/src/Metadata/v3/Metadata.ts @@ -0,0 +1,130 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { MetadataInterface } from '../types'; + +import Option from '../../codec/Option'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Text from '../../primitive/Text'; +import { flattenUniq, validateTypes } from '../util'; + +import { MetadataCall } from '../v1/Calls'; +import { MetadataEvent } from '../v1/Events'; +import { MetadataStorage } from './Storage'; + +/** + * @name MetadataModule + * @description + * The definition of a module in the system + */ +export class MetadataModule extends Struct { + constructor (value?: any) { + super({ + name: Text, + prefix: Text, + storage: Option.with(Vector.with(MetadataStorage)), + calls: Option.with(Vector.with(MetadataCall)), + events: Option.with(Vector.with(MetadataEvent)) + }, value); + } + + /** + * @description the module calls + */ + get calls (): Option> { + return this.get('calls') as Option>; + } + + /** + * @description the module events + */ + get events (): Option> { + return this.get('events') as Option>; + } + + /** + * @description the module name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description the module prefix + */ + get prefix (): Text { + return this.get('prefix') as Text; + } + + /** + * @description the associated module storage + */ + get storage (): Option> { + return this.get('storage') as Option>; + } +} + +/** + * @name MetadataV3 + * @description + * The runtime metadata as a decoded structure + */ +export default class MetadataV3 extends Struct implements MetadataInterface { + constructor (value?: any) { + super({ + modules: Vector.with(MetadataModule) + }, value); + } + + /** + * @description The associated modules for this structure + */ + get modules (): Vector { + return this.get('modules') as Vector; + } + + private get callNames () { + return this.modules.map((mod) => + mod.calls.isNone + ? [] + : mod.calls.unwrap().map((fn) => + fn.args.map((arg) => arg.type.toString()) + ) + ); + } + + private get eventNames () { + return this.modules.map((mod) => + mod.events.isNone + ? [] + : mod.events.unwrap().map((event) => + event.args.map((arg) => arg.toString()) + ) + ); + } + + private get storageNames () { + return this.modules.map((mod) => + mod.storage.isNone + ? [] + : mod.storage.unwrap().map((fn) => + fn.type.isMap + ? [fn.type.asMap.key.toString(), fn.type.asMap.value.toString()] + : [fn.type.asType.toString()] + ) + ); + } + + /** + * @description Helper to retrieve a list of all type that are found, sorted and de-deuplicated + */ + getUniqTypes (throwError: boolean): Array { + const types = flattenUniq([this.callNames, this.eventNames, this.storageNames]); + + validateTypes(types, throwError); + + return types; + } +} diff --git a/packages/types/src/Metadata/v3/Storage.ts b/packages/types/src/Metadata/v3/Storage.ts new file mode 100644 index 0000000000..072287bdea --- /dev/null +++ b/packages/types/src/Metadata/v3/Storage.ts @@ -0,0 +1,160 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../../codec/EnumType'; +import Struct from '../../codec/Struct'; +import Vector from '../../codec/Vector'; +import Bytes from '../../primitive/Bytes'; +import Text from '../../primitive/Text'; +import { MetadataStorageModifier } from '../v1/Storage'; +import { MapType, PlainType } from '../v2/Storage'; + +export class DoubleMapType extends Struct { + constructor (value?: any) { + super({ + key1: Text, + key2: Text, + value: Text, + keyHasher: Text + }, value); + } + + /** + * @description The mapped key as [[Text]] + */ + get key1 (): Text { + return this.get('key1') as Text; + } + + /** + * @description The mapped key as [[Text]] + */ + get key2 (): Text { + return this.get('key2') as Text; + } + + /** + * @description The mapped key as [[Text]] + */ + get keyHasher (): Text { + return this.get('keyHasher') as Text; + } + + /** + * @description The mapped key as [[Text]] + */ + get value (): Text { + return this.get('value') as Text; + } +} + +export class MetadataStorageType extends EnumType { + constructor (value?: any, index?: number) { + super({ + PlainType, + MapType, + DoubleMapType + }, value, index); + } + + /** + * @description The value as a mapped value + */ + get asDoubleMap (): DoubleMapType { + return this.value as DoubleMapType; + } + + /** + * @description The value as a mapped value + */ + get asMap (): MapType { + return this.value as MapType; + } + + /** + * @description The value as a [[Type]] value + */ + get asType (): PlainType { + return this.value as PlainType; + } + + /** + * @description `true` if the storage entry is a doublemap + */ + get isDoubleMap (): boolean { + return this.toNumber() === 2; + } + + /** + * @description `true` if the storage entry is a map + */ + get isMap (): boolean { + return this.toNumber() === 1; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + if (this.isDoubleMap) { + return this.asDoubleMap.toString(); + } + + return this.isMap + ? this.asMap.value.toString() + : this.asType.toString(); + } +} + +/** + * @name MetadataModule + * @description + * The definition of a storage function + */ +export class MetadataStorage extends Struct { + constructor (value?: any) { + super({ + name: Text, + modifier: MetadataStorageModifier, + type: MetadataStorageType, + fallback: Bytes, + docs: Vector.with(Text) + }, value); + } + + /** + * @description The [[Text]] documentation + */ + get docs (): Vector { + return this.get('docs') as Vector; + } + + /** + * @description The [[Bytes]] fallback default + */ + get fallback (): Bytes { + return this.get('fallback') as Bytes; + } + + /** + * @description The [[MetadataArgument]] for arguments + */ + get modifier (): MetadataStorageModifier { + return this.get('modifier') as MetadataStorageModifier; + } + + /** + * @description The call name + */ + get name (): Text { + return this.get('name') as Text; + } + + /** + * @description The [[MetadataStorageType]] + */ + get type (): MetadataStorageType { + return this.get('type') as MetadataStorageType; + } +} diff --git a/packages/types/src/Metadata/v3/index.ts b/packages/types/src/Metadata/v3/index.ts new file mode 100644 index 0000000000..c75e06fc8a --- /dev/null +++ b/packages/types/src/Metadata/v3/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { default as MetadataV3, MetadataModule as MetadataModuleV3 } from './Metadata'; + +export default MetadataV3; + +export { + MetadataModuleV3 +}; diff --git a/packages/types/src/Metadata/v3/latest.substrate.v3.json b/packages/types/src/Metadata/v3/latest.substrate.v3.json new file mode 100644 index 0000000000..01272f230b --- /dev/null +++ b/packages/types/src/Metadata/v3/latest.substrate.v3.json @@ -0,0 +1,3204 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "MetadataV3": { + "modules": [ + { + "name": "system", + "prefix": "System", + "storage": [ + { + "name": "AccountNonce", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Index", + "isLinked": false + } + }, + "fallback": "0x0000000000000000", + "docs": [ + " Extrinsics nonce for accounts." + ] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [ + " Total extrinsics count for the current block." + ] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00", + "docs": [ + " Total length in bytes for all extrinsics put together, for the current block." + ] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Hash", + "isLinked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Map of block numbers to block hashes." + ] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "MapType": { + "key": "u32", + "value": "Bytes", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Extrinsics data for the current block (maps extrinsic's index to its data)." + ] + }, + { + "name": "RandomSeed", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Random seed of the current block." + ] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Hash of the previous block." + ] + }, + { + "name": "ExtrinsicsRoot", + "modifier": "Default", + "type": { + "PlainType": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Extrinsics root of the current block, also part of the block header." + ] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "PlainType": "Digest" + }, + "fallback": "0x00", + "docs": [ + " Digest of the current block, also part of the block header." + ] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Events deposited for the current block." + ] + } + ], + "calls": null, + "events": [ + { + "name": "ExtrinsicSuccess", + "args": [], + "docs": [ + " An extrinsic completed successfully." + ] + }, + { + "name": "ExtrinsicFailed", + "args": [], + "docs": [ + " An extrinsic failed." + ] + } + ] + }, + { + "name": "aura", + "prefix": "", + "storage": null, + "calls": null, + "events": null + }, + { + "name": "timestamp", + "prefix": "Timestamp", + "storage": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current time for the current block." + ] + }, + { + "name": "BlockPeriod", + "modifier": "Optional", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x00", + "docs": [ + " Old storage item provided for compatibility. Remove after all networks upgraded." + ] + }, + { + "name": "MinimumPeriod", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0300000000000000", + "docs": [ + " The minimum period between blocks. Beware that this is different to the *expected* period", + " that the block production apparatus provides. Your chosen consensus system will generally", + " work with this to determine a sensible block time. e.g. For Aura, it will be double this", + " period on default settings." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " Did the timestamp get updated in this block?" + ] + } + ], + "calls": [ + { + "name": "set", + "args": [ + { + "name": "now", + "type": "Compact" + } + ], + "docs": [ + " Set the current time.", + "", + " This call should be invoked exactly once per block. It will panic at the finalization phase,", + " if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by `minimum_period`.", + " ", + " The dispatch origin for this call must be `Inherent`." + ] + } + ], + "events": null + }, + { + "name": "consensus", + "prefix": "Consensus", + "storage": [ + { + "name": "OriginalAuthorities", + "modifier": "Optional", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehavior." + ] + }, + { + "name": "note_offline", + "args": [ + { + "name": "offline", + "type": "InherentOfflineReport" + } + ], + "docs": [ + " Note the previous block's validator missed their opportunity to propose a block." + ] + }, + { + "name": "remark", + "args": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "docs": [ + " Make some on-chain remark." + ] + }, + { + "name": "set_heap_pages", + "args": [ + { + "name": "pages", + "type": "u64" + } + ], + "docs": [ + " Set the number of pages in the WebAssembly environment's heap." + ] + }, + { + "name": "set_code", + "args": [ + { + "name": "new", + "type": "Bytes" + } + ], + "docs": [ + " Set the new code." + ] + }, + { + "name": "set_storage", + "args": [ + { + "name": "items", + "type": "Vec" + } + ], + "docs": [ + " Set some items of storage." + ] + }, + { + "name": "kill_storage", + "args": [ + { + "name": "keys", + "type": "Vec" + } + ], + "docs": [ + " Kill some items from storage." + ] + } + ], + "events": null + }, + { + "name": "indices", + "prefix": "Indices", + "storage": [ + { + "name": "NextEnumSet", + "modifier": "Default", + "type": { + "PlainType": "AccountIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free enumeration set." + ] + }, + { + "name": "EnumSet", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountIndex", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The enumeration sets." + ] + } + ], + "calls": [], + "events": [ + { + "name": "NewAccountIndex", + "args": [ + "AccountId", + "AccountIndex" + ], + "docs": [ + " A new account index was assigned.", + "", + " This event is not triggered when an existing index is reassigned", + " to another `AccountId`." + ] + } + ] + }, + { + "name": "balances", + "prefix": "Balances", + "storage": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total units issued in the system." + ] + }, + { + "name": "ExistentialDeposit", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount required to keep an account open." + ] + }, + { + "name": "TransferFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to create an account." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": "Default", + "type": { + "PlainType": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VestingSchedule", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information regarding the vesting of a given account." + ] + }, + { + "name": "FreeBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance", + "isLinked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The 'free' balance of a given account.", + "", + " This is the only balance that matters in terms of most operations on tokens. It", + " alone is used to determine the balance when in the contract execution environment. When this", + " balance falls below the value of `ExistentialDeposit`, then the 'current account' is", + " deleted: specifically `FreeBalance`. Further, the `OnFreeBalanceZero` callback", + " is invoked, giving a chance to external modules to clean up data associated with", + " the deleted account.", + "", + " `system::AccountNonce` is also deleted if `ReservedBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`." + ] + }, + { + "name": "ReservedBalance", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Balance", + "isLinked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of the balance of a given account that is externally reserved; this can still get", + " slashed, but gets slashed last of all.", + "", + " This balance is a 'reserve' balance that other subsystems use in order to set aside tokens", + " that are still 'owned' by the account holder, but which are suspendable.", + "", + " When this balance falls below the value of `ExistentialDeposit`, then this 'reserve account'", + " is deleted: specifically, `ReservedBalance`.", + "", + " `system::AccountNonce` is also deleted if `FreeBalance` is also zero (it also gets", + " collapsed to zero if it ever becomes less than `ExistentialDeposit`.)" + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Any liquidity locks on some account balances." + ] + } + ], + "calls": [ + { + "name": "transfer", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Transfer some liquid free balance to another account.", + "", + " `transfer` will set the `FreeBalance` of the sender and receiver.", + " It will decrease the total issuance of the system by the `TransferFee`.", + " If the sender's account is below the existential deposit as a result", + " of the transfer, the account will be reaped.", + "", + " The dispatch origin for this call must be `Signed` by the transactor." + ] + }, + { + "name": "set_balance", + "args": [ + { + "name": "who", + "type": "Address" + }, + { + "name": "free", + "type": "Compact" + }, + { + "name": "reserved", + "type": "Compact" + } + ], + "docs": [ + " Set the balances of a given account.", + "", + " This will alter `FreeBalance` and `ReservedBalance` in storage.", + " If the new free or reserved balance is below the existential deposit,", + " it will also decrease the total issuance of the system (`TotalIssuance`)", + " and reset the account nonce (`system::AccountNonce`).", + "", + " The dispatch origin for this call is `root`." + ] + } + ], + "events": [ + { + "name": "NewAccount", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " A new account was created." + ] + }, + { + "name": "ReapedAccount", + "args": [ + "AccountId" + ], + "docs": [ + " An account was reaped." + ] + }, + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance", + "Balance" + ], + "docs": [ + " Transfer succeeded (from, to, value, fees)." + ] + } + ] + }, + { + "name": "session", + "prefix": "Session", + "storage": [ + { + "name": "Validators", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current set of validators." + ] + }, + { + "name": "SessionLength", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " Current length of the session." + ] + }, + { + "name": "CurrentIndex", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current index of the session." + ] + }, + { + "name": "CurrentStart", + "modifier": "Default", + "type": { + "PlainType": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Timestamp when current session started." + ] + }, + { + "name": "ForcingNewSession", + "modifier": "Optional", + "type": { + "PlainType": "bool" + }, + "fallback": "0x00", + "docs": [ + " New session is being forced is this entry exists; in which case, the boolean value is whether", + " the new session should be considered a normal rotation (rewardable) or exceptional (slashable)." + ] + }, + { + "name": "LastLengthChange", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " Block at which the session length last changed." + ] + }, + { + "name": "NextKeyFor", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "SessionKey", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The next key for a given validator." + ] + }, + { + "name": "NextSessionLength", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next session length." + ] + } + ], + "calls": [ + { + "name": "set_key", + "args": [ + { + "name": "key", + "type": "SessionKey" + } + ], + "docs": [ + " Sets the session key of `_validator` to `_key`. This doesn't take effect until the next", + " session." + ] + }, + { + "name": "set_length", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set a new session length. Won't kick in until the next session change (at current length)." + ] + }, + { + "name": "force_new_session", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Forces a new session." + ] + } + ], + "events": [ + { + "name": "NewSession", + "args": [ + "BlockNumber" + ], + "docs": [ + " New session has happened. Note that the argument is the session index, not the block", + " number as the type might suggest." + ] + } + ] + }, + { + "name": "staking", + "prefix": "Staking", + "storage": [ + { + "name": "ValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x04000000", + "docs": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "SessionsPerEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of a staking era in sessions." + ] + }, + { + "name": "SessionReward", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x3c000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "OfflineSlash", + "modifier": "Default", + "type": { + "PlainType": "Perbill" + }, + "fallback": "0x40420f00", + "docs": [ + " Slash, per validator that is taken for the first time they are found to be offline." + ] + }, + { + "name": "OfflineSlashGrace", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of instances of offline reports before slashing begins for validators." + ] + }, + { + "name": "BondingDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " The length of the bonding duration in blocks." + ] + }, + { + "name": "Invulnerables", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Any validators that may never be slashed or forcibly kicked. It's a Vec since they're easy to initialize", + " and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets." + ] + }, + { + "name": "Bonded", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountId", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Map from all locked \"stash\" accounts to the controller account." + ] + }, + { + "name": "Ledger", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "StakingLedger", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Map from all (unlocked) \"controller\" accounts to the info regarding the staking." + ] + }, + { + "name": "Payee", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "RewardDestination", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Where the reward payment should be made. Keyed by stash." + ] + }, + { + "name": "Validators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "ValidatorPrefs", + "isLinked": true + } + }, + "fallback": "0x0c00", + "docs": [ + " The map from (wannabe) validator stash key to the preferences of that validator." + ] + }, + { + "name": "Nominators", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": true + } + }, + "fallback": "0x00", + "docs": [ + " The map from nominator stash key to the set of stash keys of all validators to nominate." + ] + }, + { + "name": "Stakers", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Exposure", + "isLinked": false + } + }, + "fallback": "0x000000", + "docs": [ + " Nominators for a particular account that is in action right now. You can't iterate through validators here,", + " but you can find them in the `sessions` module.", + "", + " This is keyed by the stash account." + ] + }, + { + "name": "CurrentElected", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The currently elected validator set keyed by stash account ID." + ] + }, + { + "name": "CurrentEra", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The current era index." + ] + }, + { + "name": "CurrentSessionReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Maximum reward, per validator, that is provided per acceptable session." + ] + }, + { + "name": "CurrentEraReward", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The accumulated reward for the current era. Reset to zero at the beginning of the era and", + " increased for every successfully finished session." + ] + }, + { + "name": "NextSessionsPerEra", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [ + " The next value of sessions per era." + ] + }, + { + "name": "LastEraLengthChange", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The session index at which the era length last changed." + ] + }, + { + "name": "SlotStake", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of balance actively at stake for each validator slot, currently.", + "", + " This is used to derive rewards and punishments." + ] + }, + { + "name": "SlashCount", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "u32", + "isLinked": false + } + }, + "fallback": "0x00000000", + "docs": [ + " The number of times a given validator has been reported offline. This gets decremented by one each era that passes." + ] + }, + { + "name": "ForcingNewEra", + "modifier": "Optional", + "type": { + "PlainType": "Null" + }, + "fallback": "0x00", + "docs": [ + " We are forcing a new era." + ] + }, + { + "name": "RecentlyOffline", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber,u32)>" + }, + "fallback": "0x00", + "docs": [ + " Most recent `RECENT_OFFLINE_COUNT` instances. (who it was, when it was reported, how many instances they were offline for)." + ] + } + ], + "calls": [ + { + "name": "bond", + "args": [ + { + "name": "controller", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " Take the origin account as a stash and lock up `value` of its balance. `controller` will be the", + " account that controls it.", + "", + " The dispatch origin for this call must be _Signed_." + ] + }, + { + "name": "bond_extra", + "args": [ + { + "name": "max_additional", + "type": "Compact" + } + ], + "docs": [ + " Add some extra amount that have appeared in the stash `free_balance` into the balance up for", + " staking.", + "", + " Use this if there are additional funds in your stash account that you wish to bond.", + "", + " The dispatch origin for this call must be _Signed_ by the stash, not the controller." + ] + }, + { + "name": "unbond", + "args": [ + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Schedule a portion of the stash to be unlocked ready for transfer out after the bond", + " period ends. If this leaves an amount actively bonded less than", + " T::Currency::existential_deposit(), then it is increased to the full amount.", + "", + " Once the unlock period is done, you can call `withdraw_unbonded` to actually move", + " the funds out of management ready for transfer.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + " See also [`Call::withdraw_unbonded`]." + ] + }, + { + "name": "withdraw_unbonded", + "args": [], + "docs": [ + " Remove any unlocked chunks from the `unlocking` queue from our management.", + "", + " This essentially frees up that balance to be used by the stash account to do", + " whatever it wants.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + " See also [`Call::unbond`]." + ] + }, + { + "name": "validate", + "args": [ + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "docs": [ + " Declare the desire to validate for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "nominate", + "args": [ + { + "name": "targets", + "type": "Vec
" + } + ], + "docs": [ + " Declare the desire to nominate `targets` for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "chill", + "args": [], + "docs": [ + " Declare no desire to either validate or nominate.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "set_payee", + "args": [ + { + "name": "payee", + "type": "RewardDestination" + } + ], + "docs": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "set_controller", + "args": [ + { + "name": "controller", + "type": "Address" + } + ], + "docs": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the stash, not the controller." + ] + }, + { + "name": "set_sessions_per_era", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the number of sessions in an era." + ] + }, + { + "name": "set_bonding_duration", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The length of the bonding duration in eras." + ] + }, + { + "name": "set_validator_count", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " The ideal number of validators." + ] + }, + { + "name": "force_new_era", + "args": [ + { + "name": "apply_rewards", + "type": "bool" + } + ], + "docs": [ + " Force there to be a new era. This also forces a new session immediately after.", + " `apply_rewards` should be true for validators to get the session reward." + ] + }, + { + "name": "set_offline_slash_grace", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "docs": [ + " Set the offline slash grace period." + ] + }, + { + "name": "set_invulnerables", + "args": [ + { + "name": "validators", + "type": "Vec" + } + ], + "docs": [ + " Set the validators who cannot be slashed (if any)." + ] + } + ], + "events": [ + { + "name": "Reward", + "args": [ + "Balance" + ], + "docs": [ + " All validators have been rewarded by the given balance." + ] + }, + { + "name": "OfflineWarning", + "args": [ + "AccountId", + "u32" + ], + "docs": [ + " One validator (and their nominators) has been given a offline-warning (they're still", + " within their grace). The accrued number of slashes is recorded, too." + ] + }, + { + "name": "OfflineSlash", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " One validator (and their nominators) has been slashed by the given amount." + ] + } + ] + }, + { + "name": "democracy", + "prefix": "Democracy", + "storage": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "PlainType": "PropIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "PlainType": "Vec<(PropIndex,Proposal,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " The public proposals. Unsorted." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "PropIndex", + "value": "(BalanceOf,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Those who have locked a deposit." + ] + }, + { + "name": "LaunchPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "MinimumDeposit", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "PublicDelay", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The delay before enactment for all public referenda." + ] + }, + { + "name": "MaxLockPeriods", + "modifier": "Default", + "type": { + "PlainType": "LockPeriods" + }, + "fallback": "0x00", + "docs": [ + " The maximum number of additional lock periods a voter may offer to strengthen their vote. Multiples of `PublicDelay`." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free referendum index, aka the number of referendums started so far." + ] + }, + { + "name": "NextTally", + "modifier": "Default", + "type": { + "PlainType": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next referendum index that should be tallied." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "ReferendumInfo", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information concerning any given referendum." + ] + }, + { + "name": "DispatchQueue", + "modifier": "Default", + "type": { + "MapType": { + "key": "BlockNumber", + "value": "Vec>", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Queue of successful referenda to be dispatched." + ] + }, + { + "name": "VotersFor", + "modifier": "Default", + "type": { + "MapType": { + "key": "ReferendumIndex", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get the voters for the current proposal." + ] + }, + { + "name": "VoteOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "(ReferendumIndex,AccountId)", + "value": "Vote", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get the vote in a given referendum of a particular voter. The result is meaningful only if `voters_for` includes the", + " voter when called with the referendum (you'll get the default `Vote` value otherwise). If you don't want to check", + " `voters_for`, then you can also check for simple existence with `VoteOf::exists` first." + ] + }, + { + "name": "Delegations", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "(AccountId,LockPeriods)", + "isLinked": true + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Get the account (and lock periods) to which another account is delegating vote." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "second", + "args": [ + { + "name": "proposal", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken." + ] + }, + { + "name": "vote", + "args": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "Vote" + } + ], + "docs": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo." + ] + }, + { + "name": "start_referendum", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "threshold", + "type": "VoteThreshold" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Start a referendum." + ] + }, + { + "name": "cancel_referendum", + "args": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a referendum." + ] + }, + { + "name": "cancel_queued", + "args": [ + { + "name": "when", + "type": "Compact" + }, + { + "name": "which", + "type": "Compact" + } + ], + "docs": [ + " Cancel a proposal queued for enactment." + ] + }, + { + "name": "delegate", + "args": [ + { + "name": "to", + "type": "AccountId" + }, + { + "name": "lock_periods", + "type": "LockPeriods" + } + ], + "docs": [ + " Delegate vote." + ] + }, + { + "name": "undelegate", + "args": [], + "docs": [ + " Undelegate vote." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "PropIndex", + "Balance" + ], + "docs": [] + }, + { + "name": "Tabled", + "args": [ + "PropIndex", + "Balance", + "Vec" + ], + "docs": [] + }, + { + "name": "Started", + "args": [ + "ReferendumIndex", + "VoteThreshold" + ], + "docs": [] + }, + { + "name": "Passed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "NotPassed", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Cancelled", + "args": [ + "ReferendumIndex" + ], + "docs": [] + }, + { + "name": "Executed", + "args": [ + "ReferendumIndex", + "bool" + ], + "docs": [] + }, + { + "name": "Delegated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [] + }, + { + "name": "Undelegated", + "args": [ + "AccountId" + ], + "docs": [] + } + ] + }, + { + "name": "council", + "prefix": "Council", + "storage": [ + { + "name": "CandidacyBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x09000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBond", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " How much should be locked up in order to be able to submit votes." + ] + }, + { + "name": "PresentSlashPerVoter", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The punishment, per voter, if you provide an invalid presentation." + ] + }, + { + "name": "CarryCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x02000000", + "docs": [ + " How many runners-up should have their approvals persist until the next vote." + ] + }, + { + "name": "PresentationDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How long to give each top candidate to present themselves after the vote ends." + ] + }, + { + "name": "InactiveGracePeriod", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x01000000", + "docs": [ + " How many vote indexes need to go by after a target voter's last vote before they can be reaped if their", + " approvals are moot." + ] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "TermDuration", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0500000000000000", + "docs": [ + " How long each position is active for." + ] + }, + { + "name": "DesiredSeats", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of accounts that should be sitting on the council." + ] + }, + { + "name": "ActiveCouncil", + "modifier": "Default", + "type": { + "PlainType": "Vec<(AccountId,BlockNumber)>" + }, + "fallback": "0x00", + "docs": [ + " The current council. When there's a vote going on, this should still be used for executive", + " matters. The block number (second element in the tuple) is the block that their position is", + " active until (calculated by the sum of the block number when the council member was elected", + " and their term duration)." + ] + }, + { + "name": "VoteCount", + "modifier": "Default", + "type": { + "PlainType": "VoteIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The total number of votes that have happened or are in progress." + ] + }, + { + "name": "ApprovalsOf", + "modifier": "Default", + "type": { + "MapType": { + "key": "AccountId", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A list of votes for each voter, respecting the last cleared vote index that this voter was", + " last active at." + ] + }, + { + "name": "RegisterInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "(VoteIndex,u32)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The vote index and list slot that the candidate `who` was registered or `None` if they are not", + " currently registered." + ] + }, + { + "name": "LastActiveOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "VoteIndex", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The last cleared vote index that this voter was last active at." + ] + }, + { + "name": "Voters", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present voter list." + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The present candidate list." + ] + }, + { + "name": "CandidateCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [] + }, + { + "name": "NextFinalize", + "modifier": "Optional", + "type": { + "PlainType": "(BlockNumber,u32,Vec)" + }, + "fallback": "0x00", + "docs": [ + " The accounts holding the seats that will become free on the next tally." + ] + }, + { + "name": "SnapshotedStakes", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The stakes as they were at the point that the vote ended." + ] + }, + { + "name": "Leaderboard", + "modifier": "Optional", + "type": { + "PlainType": "Vec<(BalanceOf,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " Get the leaderboard if we;re in the presentation phase." + ] + } + ], + "calls": [ + { + "name": "set_approvals", + "args": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Set candidate approvals. Approval slots stay valid as long as candidates in those slots", + " are registered." + ] + }, + { + "name": "reap_inactive_voter", + "args": [ + { + "name": "reporter_index", + "type": "Compact" + }, + { + "name": "who", + "type": "Address" + }, + { + "name": "who_index", + "type": "Compact" + }, + { + "name": "assumed_vote_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. For it not to be a bond-consuming no-op, all approved candidate indices", + " must now be either unregistered or registered to a candidate that registered the slot after", + " the voter gave their last approval set.", + "", + " May be called by anyone. Returns the voter deposit to `signed`." + ] + }, + { + "name": "retract_voter", + "args": [ + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Remove a voter. All votes are cancelled and the voter deposit is returned." + ] + }, + { + "name": "submit_candidacy", + "args": [ + { + "name": "slot", + "type": "Compact" + } + ], + "docs": [ + " Submit oneself for candidacy.", + "", + " Account must have enough transferrable funds in it to pay the bond." + ] + }, + { + "name": "present_winner", + "args": [ + { + "name": "candidate", + "type": "Address" + }, + { + "name": "total", + "type": "Compact" + }, + { + "name": "index", + "type": "Compact" + } + ], + "docs": [ + " Claim that `signed` is one of the top Self::carry_count() + current_vote().1 candidates.", + " Only works if the `block_number >= current_vote().0` and `< current_vote().0 + presentation_duration()``", + " `signed` should have at least" + ] + }, + { + "name": "set_desired_seats", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the desired member count; if lower than the current count, then seats will not be up", + " election when they expire. If more, then a new vote will be started if one is not already", + " in progress." + ] + }, + { + "name": "remove_member", + "args": [ + { + "name": "who", + "type": "Address" + } + ], + "docs": [ + " Remove a particular member. A tally will happen instantly (if not already in a presentation", + " period) to fill the seat if removal means that the desired members are not met.", + " This is effective immediately." + ] + }, + { + "name": "set_presentation_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is currently a vote being presented for, will", + " invoke `finalize_vote`." + ] + }, + { + "name": "set_term_duration", + "args": [ + { + "name": "count", + "type": "Compact" + } + ], + "docs": [ + " Set the presentation duration. If there is current a vote being presented for, will", + " invoke `finalize_vote`." + ] + } + ], + "events": [ + { + "name": "VoterReaped", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " reaped voter, reaper" + ] + }, + { + "name": "BadReaperSlashed", + "args": [ + "AccountId" + ], + "docs": [ + " slashed reaper" + ] + }, + { + "name": "TallyStarted", + "args": [ + "u32" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has started." + ] + }, + { + "name": "TallyFinalized", + "args": [ + "Vec", + "Vec" + ], + "docs": [ + " A tally (for approval votes of council seat(s)) has ended (with one or more new members)." + ] + } + ] + }, + { + "name": "council_voting", + "prefix": "CouncilVoting", + "storage": [ + { + "name": "CooloffPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0xe803000000000000", + "docs": [] + }, + { + "name": "VotingPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0300000000000000", + "docs": [] + }, + { + "name": "EnactDelayPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Number of blocks by which to delay enactment of successful, non-unanimous-council-instigated referendum proposals." + ] + }, + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec<(BlockNumber,Hash)>" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ProposalVoters", + "modifier": "Default", + "type": { + "MapType": { + "key": "Hash", + "value": "Vec", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "CouncilVoteOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "(Hash,AccountId)", + "value": "bool", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "VetoedProposal", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(BlockNumber,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + }, + { + "name": "veto", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [] + }, + { + "name": "set_cooloff_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + }, + { + "name": "set_voting_period", + "args": [ + { + "name": "blocks", + "type": "Compact" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "TallyCancelation", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum cancellation vote.", + " Last three are yes, no, abstain counts." + ] + }, + { + "name": "TallyReferendum", + "args": [ + "Hash", + "u32", + "u32", + "u32" + ], + "docs": [ + " A voting tally has happened for a referendum vote.", + " Last three are yes, no, abstain counts." + ] + } + ] + }, + { + "name": "council_motions", + "prefix": "CouncilMotions", + "storage": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The (hashes of) the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "MapType": { + "key": "Hash", + "value": "(ProposalIndex,u32,Vec,Vec)", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Votes for a given proposal: (required_yes_votes, yes_voters, no_voters)." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + } + ], + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "u32" + ], + "docs": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given u32)." + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "u32", + "u32" + ], + "docs": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given as u32s respectively)." + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was approved by the required threshold." + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was not approved by the required threshold." + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "bool" + ], + "docs": [ + " A motion was executed; `bool` is true if returned without error." + ] + } + ] + }, + { + "name": "finality_tracker", + "prefix": "", + "storage": null, + "calls": [ + { + "name": "final_hint", + "args": [ + { + "name": "hint", + "type": "Compact" + } + ], + "docs": [ + " Hint that the author of this block thinks the best finalized", + " block is the given number." + ] + } + ], + "events": null + }, + { + "name": "grandpa", + "prefix": "GrandpaFinality", + "storage": [ + { + "name": "PendingChange", + "modifier": "Optional", + "type": { + "PlainType": "StoredPendingChange" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "NextForced", + "modifier": "Optional", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x00", + "docs": [] + } + ], + "calls": [ + { + "name": "report_misbehavior", + "args": [ + { + "name": "_report", + "type": "Bytes" + } + ], + "docs": [ + " Report some misbehavior." + ] + } + ], + "events": [ + { + "name": "NewAuthorities", + "args": [ + "Vec<(SessionKey,u64)>" + ], + "docs": [ + " New authority set has been applied." + ] + } + ] + }, + { + "name": "treasury", + "prefix": "Treasury", + "storage": [ + { + "name": "ProposalBond", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Proportion of funds that should be bonded in order to place a proposal. An accepted", + " proposal gets these back. A rejected proposal doesn't." + ] + }, + { + "name": "ProposalBondMinimum", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "modifier": "Default", + "type": { + "PlainType": "BlockNumber" + }, + "fallback": "0x0100000000000000", + "docs": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "modifier": "Default", + "type": { + "PlainType": "Permill" + }, + "fallback": "0x00000000", + "docs": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "Pot", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total funds available to this module for spending." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "PlainType": "ProposalIndex" + }, + "fallback": "0x00000000", + "docs": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "MapType": { + "key": "ProposalIndex", + "value": "Proposal", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "PlainType": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ], + "calls": [ + { + "name": "propose_spend", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "Address" + } + ], + "docs": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded." + ] + }, + { + "name": "set_pot", + "args": [ + { + "name": "new_pot", + "type": "Compact" + } + ], + "docs": [ + " Set the balance of funds available to spend." + ] + }, + { + "name": "configure", + "args": [ + { + "name": "proposal_bond", + "type": "Compact" + }, + { + "name": "proposal_bond_minimum", + "type": "Compact" + }, + { + "name": "spend_period", + "type": "Compact" + }, + { + "name": "burn", + "type": "Compact" + } + ], + "docs": [ + " (Re-)configure this module." + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Reject a proposed spend. The original deposit will be slashed." + ] + }, + { + "name": "approve_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned." + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "ProposalIndex" + ], + "docs": [ + " New proposal." + ] + }, + { + "name": "Spending", + "args": [ + "Balance" + ], + "docs": [ + " We have ended a spend period and will now allocate funds." + ] + }, + { + "name": "Awarded", + "args": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "docs": [ + " Some funds have been allocated." + ] + }, + { + "name": "Burnt", + "args": [ + "Balance" + ], + "docs": [ + " Some of our funds have been burnt." + ] + }, + { + "name": "Rollover", + "args": [ + "Balance" + ], + "docs": [ + " Spending has finished; this is the amount that rolls over until next spend." + ] + } + ] + }, + { + "name": "contract", + "prefix": "Contract", + "storage": [ + { + "name": "TransferFee", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to make a transfer." + ] + }, + { + "name": "CreationFee", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee required to create an account." + ] + }, + { + "name": "TransactionBaseFee", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the base." + ] + }, + { + "name": "TransactionByteFee", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "ContractFee", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x15000000000000000000000000000000", + "docs": [ + " The fee required to create a contract instance." + ] + }, + { + "name": "CallBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x8700000000000000", + "docs": [ + " The base fee charged for calling into a contract." + ] + }, + { + "name": "CreateBaseFee", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0xaf00000000000000", + "docs": [ + " The base fee charged for creating a contract." + ] + }, + { + "name": "GasPrice", + "modifier": "Default", + "type": { + "PlainType": "BalanceOf" + }, + "fallback": "0x01000000000000000000000000000000", + "docs": [ + " The price of one unit of gas." + ] + }, + { + "name": "MaxDepth", + "modifier": "Default", + "type": { + "PlainType": "u32" + }, + "fallback": "0x64000000", + "docs": [ + " The maximum nesting level of a call/create stack." + ] + }, + { + "name": "BlockGasLimit", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x40420f0000000000", + "docs": [ + " The maximum amount of gas that could be expended per block." + ] + }, + { + "name": "GasSpent", + "modifier": "Default", + "type": { + "PlainType": "Gas" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Gas spent so far in this block." + ] + }, + { + "name": "CurrentSchedule", + "modifier": "Default", + "type": { + "PlainType": "Schedule" + }, + "fallback": "0x000000000100000000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000000010010000000", + "docs": [ + " Current cost schedule for contracts." + ] + }, + { + "name": "CodeHashOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "CodeHash", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The code associated with a given account." + ] + }, + { + "name": "PristineCode", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "Bytes", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A mapping from an original code hash to the original code, untouched by instrumentation." + ] + }, + { + "name": "CodeStorage", + "modifier": "Optional", + "type": { + "MapType": { + "key": "CodeHash", + "value": "PrefabWasmModule", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " A mapping between an original code hash and instrumented wasm code, ready for the execution." + ] + }, + { + "name": "AccountCounter", + "modifier": "Default", + "type": { + "PlainType": "u64" + }, + "fallback": "0x0000000000000000", + "docs": [ + " The subtrie counter" + ] + }, + { + "name": "AccountInfoOf", + "modifier": "Optional", + "type": { + "MapType": { + "key": "AccountId", + "value": "AccountInfo", + "isLinked": false + } + }, + "fallback": "0x00", + "docs": [ + " The code associated with a given account." + ] + } + ], + "calls": [ + { + "name": "update_schedule", + "args": [ + { + "name": "schedule", + "type": "Schedule" + } + ], + "docs": [ + " Updates the schedule for metering contracts.", + "", + " The schedule must have a greater version than the stored schedule." + ] + }, + { + "name": "put_code", + "args": [ + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [ + " Stores the given binary Wasm code into the chains storage and returns its `codehash`.", + " You can instantiate contracts only with stored code." + ] + }, + { + "name": "call", + "args": [ + { + "name": "dest", + "type": "Address" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Makes a call to an account, optionally transferring some balance.", + "", + " * If the account is a smart-contract account, the associated code will be", + " executed and any value will be transferred.", + " * If the account is a regular account, any value will be transferred.", + " * If no account exists and the call value is not less than `existential_deposit`,", + " a regular account will be created and any value will be transferred." + ] + }, + { + "name": "create", + "args": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code_hash", + "type": "CodeHash" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "docs": [ + " Creates a new contract from the `codehash` generated by `put_code`, optionally transferring some balance.", + "", + " Creation is executed as follows:", + "", + " - the destination address is computed based on the sender and hash of the code.", + " - the smart-contract account is created at the computed address.", + " - the `ctor_code` is executed in the context of the newly created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any call received by this account.", + " - The contract is initialized." + ] + } + ], + "events": [ + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " Transfer happened `from` to `to` with given `value` as part of a `call` or `create`." + ] + }, + { + "name": "Instantiated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " Contract deployed by address at the specified address." + ] + }, + { + "name": "CodeStored", + "args": [ + "Hash" + ], + "docs": [ + " Code with the specified hash has been stored." + ] + }, + { + "name": "ScheduleUpdated", + "args": [ + "u32" + ], + "docs": [ + " Triggered when the current schedule is updated." + ] + }, + { + "name": "Dispatched", + "args": [ + "AccountId", + "bool" + ], + "docs": [ + " A call was dispatched from the given account. The bool signals whether it was", + " successful execution or not." + ] + } + ] + }, + { + "name": "sudo", + "prefix": "Sudo", + "storage": [ + { + "name": "Key", + "modifier": "Default", + "type": { + "PlainType": "AccountId" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + } + ], + "calls": [ + { + "name": "sudo", + "args": [ + { + "name": "proposal", + "type": "Proposal" + } + ], + "docs": [] + }, + { + "name": "set_key", + "args": [ + { + "name": "new", + "type": "Address" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "Sudid", + "args": [ + "bool" + ], + "docs": [ + " A sudo just took place." + ] + }, + { + "name": "KeyChanged", + "args": [ + "AccountId" + ], + "docs": [ + " The sudoer just switched identity; the old key is supplied." + ] + } + ] + } + ] + } + } +} diff --git a/packages/types/src/Metadata/v3/static.ts b/packages/types/src/Metadata/v3/static.ts new file mode 100644 index 0000000000..ff0c466f40 --- /dev/null +++ b/packages/types/src/Metadata/v3/static.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const meta = '0x6d65746103441873797374656d1853797374656d012c304163636f756e744e6f6e6365010130543a3a4163636f756e74496420543a3a496e64657800200000000000000000047c2045787472696e73696373206e6f6e636520666f72206163636f756e74732e3845787472696e736963436f756e7400000c753332040004b820546f74616c2065787472696e7369637320636f756e7420666f72207468652063757272656e7420626c6f636b2e40416c6c45787472696e736963734c656e00000c753332040004390120546f74616c206c656e67746820696e20627974657320666f7220616c6c2065787472696e736963732070757420746f6765746865722c20666f72207468652063757272656e7420626c6f636b2e24426c6f636b48617368010138543a3a426c6f636b4e756d6265721c543a3a48617368008000000000000000000000000000000000000000000000000000000000000000000498204d6170206f6620626c6f636b206e756d6265727320746f20626c6f636b206861736865732e3445787472696e7369634461746101010c7533321c5665633c75383e0004000431012045787472696e73696373206461746120666f72207468652063757272656e7420626c6f636b20286d6170732065787472696e736963277320696e64657820746f206974732064617461292e2852616e646f6d5365656401001c543a3a4861736880000000000000000000000000000000000000000000000000000000000000000004882052616e646f6d2073656564206f66207468652063757272656e7420626c6f636b2e184e756d626572010038543a3a426c6f636b4e756d626572200000000000000000040901205468652063757272656e7420626c6f636b206e756d626572206265696e672070726f6365737365642e205365742062792060657865637574655f626c6f636b602e28506172656e744861736801001c543a3a4861736880000000000000000000000000000000000000000000000000000000000000000004702048617368206f66207468652070726576696f757320626c6f636b2e3845787472696e73696373526f6f7401001c543a3a486173688000000000000000000000000000000000000000000000000000000000000000000415012045787472696e7369637320726f6f74206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e18446967657374010024543a3a446967657374040004f020446967657374206f66207468652063757272656e7420626c6f636b2c20616c736f2070617274206f662074686520626c6f636b206865616465722e184576656e74730100685665633c4576656e745265636f72643c543a3a4576656e743e3e040004a0204576656e7473206465706f736974656420666f72207468652063757272656e7420626c6f636b2e0001084045787472696e7369635375636365737300049420416e2065787472696e73696320636f6d706c65746564207375636365737366756c6c792e3c45787472696e7369634661696c656400045420416e2065787472696e736963206661696c65642e1061757261000000002474696d657374616d702454696d657374616d7001100c4e6f77010024543a3a4d6f6d656e7420000000000000000004902043757272656e742074696d6520666f72207468652063757272656e7420626c6f636b2e2c426c6f636b506572696f64000024543a3a4d6f6d656e740400044501204f6c642073746f72616765206974656d2070726f766964656420666f7220636f6d7061746962696c6974792e2052656d6f766520616674657220616c6c206e6574776f726b732075706772616465642e344d696e696d756d506572696f64010024543a3a4d6f6d656e7420030000000000000010690120546865206d696e696d756d20706572696f64206265747765656e20626c6f636b732e204265776172652074686174207468697320697320646966666572656e7420746f20746865202a65787065637465642a20706572696f64690120746861742074686520626c6f636b2070726f64756374696f6e206170706172617475732070726f76696465732e20596f75722063686f73656e20636f6e73656e7375732073797374656d2077696c6c2067656e6572616c6c79650120776f726b2077697468207468697320746f2064657465726d696e6520612073656e7369626c6520626c6f636b2074696d652e20652e672e20466f7220417572612c2069742077696c6c20626520646f75626c6520746869737020706572696f64206f6e2064656661756c742073657474696e67732e24446964557064617465010010626f6f6c040004b420446964207468652074696d657374616d7020676574207570646174656420696e207468697320626c6f636b3f01040c736574040c6e6f7748436f6d706163743c543a3a4d6f6d656e743e205820536574207468652063757272656e742074696d652e00750120546869732063616c6c2073686f756c6420626520696e766f6b65642065786163746c79206f6e63652070657220626c6f636b2e2049742077696c6c2070616e6963206174207468652066696e616c697a6174696f6e2070686173652cbc20696620746869732063616c6c206861736e2774206265656e20696e766f6b656420627920746861742074696d652e008d01205468652074696d657374616d702073686f756c642062652067726561746572207468616e207468652070726576696f7573206f6e652062792074686520616d6f756e742073706563696669656420627920606d696e696d756d5f706572696f64602e0420d820546865206469737061746368206f726967696e20666f7220746869732063616c6c206d7573742062652060496e686572656e74602e0024636f6e73656e73757324436f6e73656e73757301044c4f726967696e616c417574686f7269746965730000485665633c543a3a53657373696f6e4b65793e040000011c487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0464205265706f727420736f6d65206d69736265686176696f722e306e6f74655f6f66666c696e65041c6f66666c696e65f43c543a3a496e686572656e744f66666c696e655265706f727420617320496e686572656e744f66666c696e655265706f72743e3a3a496e686572656e74044501204e6f7465207468652070726576696f757320626c6f636b27732076616c696461746f72206d6973736564207468656972206f70706f7274756e69747920746f2070726f706f7365206120626c6f636b2e1872656d61726b041c5f72656d61726b1c5665633c75383e046c204d616b6520736f6d65206f6e2d636861696e2072656d61726b2e387365745f686561705f7061676573041470616765730c75363404fc2053657420746865206e756d626572206f6620706167657320696e2074686520576562417373656d626c7920656e7669726f6e6d656e74277320686561702e207365745f636f6465040c6e65771c5665633c75383e04482053657420746865206e657720636f64652e2c7365745f73746f7261676504146974656d73345665633c4b657956616c75653e046c2053657420736f6d65206974656d73206f662073746f726167652e306b696c6c5f73746f7261676504106b657973205665633c4b65793e0478204b696c6c20736f6d65206974656d732066726f6d2073746f726167652e001c696e64696365731c496e646963657301082c4e657874456e756d53657401003c543a3a4163636f756e74496e6465781000000000047c20546865206e657874206672656520656e756d65726174696f6e207365742e1c456e756d53657401013c543a3a4163636f756e74496e646578445665633c543a3a4163636f756e7449643e00040004582054686520656e756d65726174696f6e20736574732e010001043c4e65774163636f756e74496e64657808244163636f756e744964304163636f756e74496e64657810882041206e6577206163636f756e7420696e646578207761732061737369676e65642e0005012054686973206576656e74206973206e6f7420747269676765726564207768656e20616e206578697374696e6720696e64657820697320726561737369676e65646020746f20616e6f7468657220604163636f756e744964602e2062616c616e6365732042616c616e636573012834546f74616c49737375616e6365010028543a3a42616c616e6365400000000000000000000000000000000004982054686520746f74616c20756e6974732069737375656420696e207468652073797374656d2e484578697374656e7469616c4465706f736974010028543a3a42616c616e6365400000000000000000000000000000000004d420546865206d696e696d756d20616d6f756e7420726571756972656420746f206b65657020616e206163636f756e74206f70656e2e2c5472616e73666572466565010028543a3a42616c616e636540000000000000000000000000000000000494205468652066656520726571756972656420746f206d616b652061207472616e736665722e2c4372656174696f6e466565010028543a3a42616c616e63654000000000000000000000000000000000049c205468652066656520726571756972656420746f2063726561746520616e206163636f756e742e485472616e73616374696f6e42617365466565010028543a3a42616c616e6365400000000000000000000000000000000004dc205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b2074686520626173652e485472616e73616374696f6e42797465466565010028543a3a42616c616e63654000000000000000000000000000000000040d01205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b20746865207065722d6279746520706f7274696f6e2e1c56657374696e67000130543a3a4163636f756e7449646c56657374696e675363686564756c653c543a3a42616c616e63653e00040004d820496e666f726d6174696f6e20726567617264696e67207468652076657374696e67206f66206120676976656e206163636f756e742e2c4672656542616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e63650040000000000000000000000000000000002c9c20546865202766726565272062616c616e6365206f66206120676976656e206163636f756e742e004101205468697320697320746865206f6e6c792062616c616e63652074686174206d61747465727320696e207465726d73206f66206d6f7374206f7065726174696f6e73206f6e20746f6b656e732e204974750120616c6f6e65206973207573656420746f2064657465726d696e65207468652062616c616e6365207768656e20696e2074686520636f6e747261637420657865637574696f6e20656e7669726f6e6d656e742e205768656e207468697355012062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e20746865202763757272656e74206163636f756e74272069733d012064656c657465643a207370656369666963616c6c7920604672656542616c616e6365602e20467572746865722c2074686520604f6e4672656542616c616e63655a65726f602063616c6c6261636b450120697320696e766f6b65642c20676976696e672061206368616e636520746f2065787465726e616c206d6f64756c657320746f20636c65616e2075702064617461206173736f636961746564207769746854207468652064656c65746564206163636f756e742e005d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c657465642069662060526573657276656442616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473150120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e3c526573657276656442616c616e6365010130543a3a4163636f756e74496428543a3a42616c616e63650040000000000000000000000000000000002c75012054686520616d6f756e74206f66207468652062616c616e6365206f66206120676976656e206163636f756e7420746861742069732065787465726e616c6c792072657365727665643b20746869732063616e207374696c6c206765749c20736c61736865642c20627574206765747320736c6173686564206c617374206f6620616c6c2e006d0120546869732062616c616e63652069732061202772657365727665272062616c616e63652074686174206f746865722073756273797374656d732075736520696e206f7264657220746f2073657420617369646520746f6b656e732501207468617420617265207374696c6c20276f776e65642720627920746865206163636f756e7420686f6c6465722c20627574207768696368206172652073757370656e6461626c652e007501205768656e20746869732062616c616e63652066616c6c732062656c6f77207468652076616c7565206f6620604578697374656e7469616c4465706f736974602c207468656e2074686973202772657365727665206163636f756e7427b42069732064656c657465643a207370656369666963616c6c792c2060526573657276656442616c616e6365602e004d01206073797374656d3a3a4163636f756e744e6f6e63656020697320616c736f2064656c6574656420696620604672656542616c616e63656020697320616c736f207a65726f2028697420616c736f2067657473190120636f6c6c617073656420746f207a65726f2069662069742065766572206265636f6d6573206c657373207468616e20604578697374656e7469616c4465706f736974602e29144c6f636b73010130543a3a4163636f756e744964b05665633c42616c616e63654c6f636b3c543a3a42616c616e63652c20543a3a426c6f636b4e756d6265723e3e00040004b820416e79206c6971756964697479206c6f636b73206f6e20736f6d65206163636f756e742062616c616e6365732e0108207472616e736665720810646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c75654c436f6d706163743c543a3a42616c616e63653e20d8205472616e7366657220736f6d65206c697175696420667265652062616c616e636520746f20616e6f74686572206163636f756e742e00090120607472616e73666572602077696c6c207365742074686520604672656542616c616e636560206f66207468652073656e64657220616e642072656365697665722e21012049742077696c6c2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d2062792074686520605472616e73666572466565602e1501204966207468652073656e6465722773206163636f756e742069732062656c6f7720746865206578697374656e7469616c206465706f736974206173206120726573756c74b4206f6620746865207472616e736665722c20746865206163636f756e742077696c6c206265207265617065642e00190120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d75737420626520605369676e65646020627920746865207472616e736163746f722e2c7365745f62616c616e63650c0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636510667265654c436f6d706163743c543a3a42616c616e63653e2072657365727665644c436f6d706163743c543a3a42616c616e63653e209420536574207468652062616c616e636573206f66206120676976656e206163636f756e742e00010120546869732077696c6c20616c74657220604672656542616c616e63656020616e642060526573657276656442616c616e63656020696e2073746f726167652e190120496620746865206e65772066726565206f722072657365727665642062616c616e63652069732062656c6f7720746865206578697374656e7469616c206465706f7369742c25012069742077696c6c20616c736f2064656372656173652074686520746f74616c2069737375616e6365206f66207468652073797374656d202860546f74616c49737375616e63656029d820616e6420726573657420746865206163636f756e74206e6f6e636520286073797374656d3a3a4163636f756e744e6f6e636560292e00b420546865206469737061746368206f726967696e20666f7220746869732063616c6c2069732060726f6f74602e010c284e65774163636f756e7408244163636f756e7449641c42616c616e6365046c2041206e6577206163636f756e742077617320637265617465642e345265617065644163636f756e7404244163636f756e744964045c20416e206163636f756e7420776173207265617065642e205472616e7366657210244163636f756e744964244163636f756e7449641c42616c616e63651c42616c616e636504b0205472616e7366657220737563636565646564202866726f6d2c20746f2c2076616c75652c2066656573292e1c73657373696f6e1c53657373696f6e01202856616c696461746f72730100445665633c543a3a4163636f756e7449643e0400047c205468652063757272656e7420736574206f662076616c696461746f72732e3453657373696f6e4c656e677468010038543a3a426c6f636b4e756d62657220e803000000000000047c2043757272656e74206c656e677468206f66207468652073657373696f6e2e3043757272656e74496e646578010038543a3a426c6f636b4e756d62657220000000000000000004782043757272656e7420696e646578206f66207468652073657373696f6e2e3043757272656e745374617274010024543a3a4d6f6d656e7420000000000000000004a02054696d657374616d70207768656e2063757272656e742073657373696f6e20737461727465642e44466f7263696e674e657753657373696f6e000010626f6f6c0400087901204e65772073657373696f6e206973206265696e6720666f72636564206973207468697320656e747279206578697374733b20696e20776869636820636173652c2074686520626f6f6c65616e2076616c75652069732077686574686572810120746865206e65772073657373696f6e2073686f756c6420626520636f6e736964657265642061206e6f726d616c20726f746174696f6e202872657761726461626c6529206f7220657863657074696f6e616c2028736c61736861626c65292e404c6173744c656e6774684368616e6765000038543a3a426c6f636b4e756d626572040004c020426c6f636b206174207768696368207468652073657373696f6e206c656e677468206c617374206368616e6765642e284e6578744b6579466f72000130543a3a4163636f756e74496434543a3a53657373696f6e4b6579000400049020546865206e657874206b657920666f72206120676976656e2076616c696461746f722e444e65787453657373696f6e4c656e677468000038543a3a426c6f636b4e756d6265720400046420546865206e6578742073657373696f6e206c656e6774682e010c1c7365745f6b6579040c6b657934543a3a53657373696f6e4b65790861012053657473207468652073657373696f6e206b6579206f6620605f76616c696461746f726020746f20605f6b6579602e205468697320646f65736e27742074616b652065666665637420756e74696c20746865206e657874242073657373696f6e2e287365745f6c656e677468040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e046d01205365742061206e65772073657373696f6e206c656e6774682e20576f6e2774206b69636b20696e20756e74696c20746865206e6578742073657373696f6e206368616e6765202861742063757272656e74206c656e677468292e44666f7263655f6e65775f73657373696f6e04346170706c795f7265776172647310626f6f6c045820466f726365732061206e65772073657373696f6e2e0104284e657753657373696f6e042c426c6f636b4e756d626572085501204e65772073657373696f6e206861732068617070656e65642e204e6f746520746861742074686520617267756d656e74206973207468652073657373696f6e20696e6465782c206e6f742074686520626c6f636b88206e756d626572206173207468652074797065206d6967687420737567676573742e1c7374616b696e671c5374616b696e6701603856616c696461746f72436f756e7401000c753332100000000004a82054686520696465616c206e756d626572206f66207374616b696e67207061727469636970616e74732e544d696e696d756d56616c696461746f72436f756e7401000c7533321004000000044101204d696e696d756d206e756d626572206f66207374616b696e67207061727469636970616e7473206265666f726520656d657267656e637920636f6e646974696f6e732061726520696d706f7365642e3853657373696f6e73506572457261010038543a3a426c6f636b4e756d62657220e80300000000000004a420546865206c656e677468206f662061207374616b696e672065726120696e2073657373696f6e732e3453657373696f6e52657761726401001c50657262696c6c103c000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e304f66666c696e65536c61736801001c50657262696c6c1040420f0004510120536c6173682c207065722076616c696461746f7220746861742069732074616b656e20666f72207468652066697273742074696d6520746865792061726520666f756e6420746f206265206f66666c696e652e444f66666c696e65536c617368477261636501000c7533321000000000043901204e756d626572206f6620696e7374616e636573206f66206f66666c696e65207265706f727473206265666f726520736c617368696e6720626567696e7320666f722076616c696461746f72732e3c426f6e64696e674475726174696f6e010038543a3a426c6f636b4e756d62657220e80300000000000004b820546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20626c6f636b732e34496e76756c6e657261626c65730100445665633c543a3a4163636f756e7449643e040008a50120416e792076616c696461746f72732074686174206d6179206e6576657220626520736c6173686564206f7220666f726369626c79206b69636b65642e20497427732061205665632073696e63652074686579277265206561737920746f20696e697469616c697a65ad0120616e642074686520706572666f726d616e636520686974206973206d696e696d616c2028776520657870656374206e6f206d6f7265207468616e20666f757220696e76756c6e657261626c65732920616e64207265737472696374656420746f20746573746e6574732e18426f6e646564000130543a3a4163636f756e74496430543a3a4163636f756e744964000400040101204d61702066726f6d20616c6c206c6f636b65642022737461736822206163636f756e747320746f2074686520636f6e74726f6c6c6572206163636f756e742e184c6564676572000130543a3a4163636f756e744964e45374616b696e674c65646765723c543a3a4163636f756e7449642c2042616c616e63654f663c543e2c20543a3a426c6f636b4e756d6265723e000400044501204d61702066726f6d20616c6c2028756e6c6f636b6564292022636f6e74726f6c6c657222206163636f756e747320746f2074686520696e666f20726567617264696e6720746865207374616b696e672e145061796565010130543a3a4163636f756e7449644452657761726444657374696e6174696f6e00040004e42057686572652074686520726577617264207061796d656e742073686f756c64206265206d6164652e204b657965642062792073746173682e2856616c696461746f7273010130543a3a4163636f756e7449647056616c696461746f7250726566733c42616c616e63654f663c543e3e01080c0004450120546865206d61702066726f6d202877616e6e616265292076616c696461746f72207374617368206b657920746f2074686520707265666572656e636573206f6620746861742076616c696461746f722e284e6f6d696e61746f7273010130543a3a4163636f756e744964445665633c543a3a4163636f756e7449643e01040004650120546865206d61702066726f6d206e6f6d696e61746f72207374617368206b657920746f2074686520736574206f66207374617368206b657973206f6620616c6c2076616c696461746f727320746f206e6f6d696e6174652e1c5374616b657273010130543a3a4163636f756e744964904578706f737572653c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e000c00000010b101204e6f6d696e61746f727320666f72206120706172746963756c6172206163636f756e74207468617420697320696e20616374696f6e207269676874206e6f772e20596f752063616e27742069746572617465207468726f7567682076616c696461746f727320686572652cc02062757420796f752063616e2066696e64207468656d20696e20746865206073657373696f6e7360206d6f64756c652e00902054686973206973206b6579656420627920746865207374617368206163636f756e742e3843757272656e74456c65637465640100445665633c543a3a4163636f756e7449643e040004fc205468652063757272656e746c7920656c65637465642076616c696461746f7220736574206b65796564206279207374617368206163636f756e742049442e2843757272656e74457261010038543a3a426c6f636b4e756d626572200000000000000000045c205468652063757272656e742065726120696e6465782e5043757272656e7453657373696f6e52657761726401003042616c616e63654f663c543e4000000000000000000000000000000000042101204d6178696d756d207265776172642c207065722076616c696461746f722c20746861742069732070726f7669646564207065722061636365707461626c652073657373696f6e2e4043757272656e7445726152657761726401003042616c616e63654f663c543e40000000000000000000000000000000000869012054686520616363756d756c617465642072657761726420666f72207468652063757272656e74206572612e20526573657420746f207a65726f2061742074686520626567696e6e696e67206f66207468652065726120616e64cc20696e6372656173656420666f72206576657279207375636365737366756c6c792066696e69736865642073657373696f6e2e484e65787453657373696f6e73506572457261000038543a3a426c6f636b4e756d6265720400049020546865206e6578742076616c7565206f662073657373696f6e7320706572206572612e4c4c6173744572614c656e6774684368616e6765010038543a3a426c6f636b4e756d62657220000000000000000004e0205468652073657373696f6e20696e6465782061742077686963682074686520657261206c656e677468206c617374206368616e6765642e24536c6f745374616b6501003042616c616e63654f663c543e40000000000000000000000000000000000c31012054686520616d6f756e74206f662062616c616e6365206163746976656c79206174207374616b6520666f7220656163682076616c696461746f7220736c6f742c2063757272656e746c792e00c02054686973206973207573656420746f20646572697665207265776172647320616e642070756e6973686d656e74732e28536c617368436f756e74010130543a3a4163636f756e7449640c75333200100000000004d10120546865206e756d626572206f662074696d6573206120676976656e2076616c696461746f7220686173206265656e207265706f72746564206f66666c696e652e205468697320676574732064656372656d656e746564206279206f6e652065616368206572612074686174207061737365732e34466f7263696e674e65774572610000082829040004682057652061726520666f7263696e672061206e6577206572612e3c526563656e746c794f66666c696e650100a05665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d6265722c20753332293e040004f101204d6f737420726563656e742060524543454e545f4f46464c494e455f434f554e546020696e7374616e6365732e202877686f206974207761732c207768656e20697420776173207265706f727465642c20686f77206d616e7920696e7374616e63657320746865792077657265206f66666c696e6520666f72292e013c10626f6e640c28636f6e74726f6c6c65728c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c756554436f6d706163743c42616c616e63654f663c543e3e1470617965654452657761726444657374696e6174696f6e1081012054616b6520746865206f726967696e206163636f756e74206173206120737461736820616e64206c6f636b207570206076616c756560206f66206974732062616c616e63652e2060636f6e74726f6c6c6572602077696c6c2062652074686568206163636f756e74207468617420636f6e74726f6c732069742e00d020546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2e28626f6e645f657874726104386d61785f6164646974696f6e616c54436f6d706163743c42616c616e63654f663c543e3e1875012041646420736f6d6520657874726120616d6f756e742074686174206861766520617070656172656420696e207468652073746173682060667265655f62616c616e63656020696e746f207468652062616c616e636520757020666f7224207374616b696e672e00510120557365207468697320696620746865726520617265206164646974696f6e616c2066756e647320696e20796f7572207374617368206163636f756e74207468617420796f75207769736820746f20626f6e642e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e18756e626f6e64041476616c756554436f6d706163743c42616c616e63654f663c543e3e285501205363686564756c65206120706f7274696f6e206f662074686520737461736820746f20626520756e6c6f636b656420726561647920666f72207472616e73666572206f75742061667465722074686520626f6e64010120706572696f6420656e64732e2049662074686973206c656176657320616e20616d6f756e74206163746976656c7920626f6e646564206c657373207468616e350120543a3a43757272656e63793a3a6578697374656e7469616c5f6465706f73697428292c207468656e20697420697320696e6372656173656420746f207468652066756c6c20616d6f756e742e004901204f6e63652074686520756e6c6f636b20706572696f6420697320646f6e652c20796f752063616e2063616c6c206077697468647261775f756e626f6e6465646020746f2061637475616c6c79206d6f7665c0207468652066756e6473206f7574206f66206d616e6167656d656e7420726561647920666f72207472616e736665722e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e00982053656520616c736f205b6043616c6c3a3a77697468647261775f756e626f6e646564605d2e4477697468647261775f756e626f6e64656400202d012052656d6f766520616e7920756e6c6f636b6564206368756e6b732066726f6d207468652060756e6c6f636b696e67602071756575652066726f6d206f7572206d616e6167656d656e742e003501205468697320657373656e7469616c6c7920667265657320757020746861742062616c616e636520746f206265207573656420627920746865207374617368206163636f756e7420746f20646f4c2077686174657665722069742077616e74732e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e006c2053656520616c736f205b6043616c6c3a3a756e626f6e64605d2e2076616c6964617465041470726566737056616c696461746f7250726566733c42616c616e63654f663c543e3e14e8204465636c617265207468652064657369726520746f2076616c696461746520666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e206e6f6d696e617465041c74617267657473a05665633c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263653e141101204465636c617265207468652064657369726520746f206e6f6d696e6174652060746172676574736020666f7220746865206f726967696e20636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e146368696c6c0014c8204465636c617265206e6f2064657369726520746f206569746865722076616c6964617465206f72206e6f6d696e6174652e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e247365745f7061796565041470617965654452657761726444657374696e6174696f6e14b8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f2062792074686520636f6e74726f6c6c65722c206e6f74207468652073746173682e387365745f636f6e74726f6c6c65720428636f6e74726f6c6c65728c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514b8202852652d2973657420746865207061796d656e742074617267657420666f72206120636f6e74726f6c6c65722e00dc20456666656374732077696c6c2062652066656c742061742074686520626567696e6e696e67206f6620746865206e657874206572612e00550120546865206469737061746368206f726967696e20666f7220746869732063616c6c206d757374206265205f5369676e65645f206279207468652073746173682c206e6f742074686520636f6e74726f6c6c65722e507365745f73657373696f6e735f7065725f657261040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04982053657420746865206e756d626572206f662073657373696f6e7320696e20616e206572612e507365745f626f6e64696e675f6475726174696f6e040c6e65775c436f6d706163743c543a3a426c6f636b4e756d6265723e04b020546865206c656e677468206f662074686520626f6e64696e67206475726174696f6e20696e20657261732e4c7365745f76616c696461746f725f636f756e74040c6e657730436f6d706163743c7533323e04802054686520696465616c206e756d626572206f662076616c696461746f72732e34666f7263655f6e65775f65726104346170706c795f7265776172647310626f6f6c083d0120466f72636520746865726520746f2062652061206e6577206572612e205468697320616c736f20666f726365732061206e65772073657373696f6e20696d6d6564696174656c792061667465722e250120606170706c795f72657761726473602073686f756c64206265207472756520666f722076616c696461746f727320746f20676574207468652073657373696f6e207265776172642e5c7365745f6f66666c696e655f736c6173685f6772616365040c6e657730436f6d706163743c7533323e04902053657420746865206f66666c696e6520736c61736820677261636520706572696f642e447365745f696e76756c6e657261626c6573042876616c696461746f7273445665633c543a3a4163636f756e7449643e04cc20536574207468652076616c696461746f72732077686f2063616e6e6f7420626520736c61736865642028696620616e79292e010c18526577617264041c42616c616e636504e020416c6c2076616c696461746f72732068617665206265656e2072657761726465642062792074686520676976656e2062616c616e63652e384f66666c696e655761726e696e6708244163636f756e7449640c753332085501204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20676976656e2061206f66666c696e652d7761726e696e67202874686579277265207374696c6c15012077697468696e207468656972206772616365292e205468652061636372756564206e756d626572206f6620736c6173686573206973207265636f726465642c20746f6f2e304f66666c696e65536c61736808244163636f756e7449641c42616c616e6365042d01204f6e652076616c696461746f722028616e64207468656972206e6f6d696e61746f72732920686173206265656e20736c61736865642062792074686520676976656e20616d6f756e742e2464656d6f63726163792444656d6f6372616379013c3c5075626c696350726f70436f756e7401002450726f70496e646578100000000004f420546865206e756d626572206f6620287075626c6963292070726f706f73616c7320746861742068617665206265656e206d61646520736f206661722e2c5075626c696350726f70730100ac5665633c2850726f70496e6465782c20543a3a50726f706f73616c2c20543a3a4163636f756e744964293e0400048020546865207075626c69632070726f706f73616c732e20556e736f727465642e244465706f7369744f6600012450726f70496e646578842842616c616e63654f663c543e2c205665633c543a3a4163636f756e7449643e2900040004842054686f73652077686f2068617665206c6f636b65642061206465706f7369742e304c61756e6368506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004e420486f77206f6674656e2028696e20626c6f636b7329206e6577207075626c6963207265666572656e646120617265206c61756e636865642e384d696e696d756d4465706f73697401003042616c616e63654f663c543e400000000000000000000000000000000004350120546865206d696e696d756d20616d6f756e7420746f20626520757365642061732061206465706f73697420666f722061207075626c6963207265666572656e64756d2070726f706f73616c2e2c5075626c696344656c6179010038543a3a426c6f636b4e756d62657220000000000000000004d4205468652064656c6179206265666f726520656e6163746d656e7420666f7220616c6c207075626c6963207265666572656e64612e384d61784c6f636b506572696f647301002c4c6f636b506572696f6473040004d90120546865206d6178696d756d206e756d626572206f66206164646974696f6e616c206c6f636b20706572696f6473206120766f746572206d6179206f6666657220746f20737472656e677468656e20746865697220766f74652e204d756c7469706c6573206f6620605075626c696344656c6179602e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e3c5265666572656e64756d436f756e7401003c5265666572656e64756d496e646578100000000004390120546865206e6578742066726565207265666572656e64756d20696e6465782c20616b6120746865206e756d626572206f66207265666572656e64756d73207374617274656420736f206661722e244e65787454616c6c7901003c5265666572656e64756d496e646578100000000004c820546865206e657874207265666572656e64756d20696e64657820746861742073686f756c642062652074616c6c6965642e405265666572656e64756d496e666f4f6600013c5265666572656e64756d496e646578b4285265666572656e64756d496e666f3c543a3a426c6f636b4e756d6265722c20543a3a50726f706f73616c3e2900040004b420496e666f726d6174696f6e20636f6e6365726e696e6720616e7920676976656e207265666572656e64756d2e3444697370617463685175657565010138543a3a426c6f636b4e756d626572ac5665633c4f7074696f6e3c28543a3a50726f706f73616c2c205265666572656e64756d496e646578293e3e00040004c0205175657565206f66207375636365737366756c207265666572656e646120746f20626520646973706174636865642e24566f74657273466f7201013c5265666572656e64756d496e646578445665633c543a3a4163636f756e7449643e00040004a4204765742074686520766f7465727320666f72207468652063757272656e742070726f706f73616c2e18566f74654f6601017c285265666572656e64756d496e6465782c20543a3a4163636f756e7449642910566f74650004000cd501204765742074686520766f746520696e206120676976656e207265666572656e64756d206f66206120706172746963756c617220766f7465722e2054686520726573756c74206973206d65616e696e6766756c206f6e6c792069662060766f746572735f666f726020696e636c7564657320746865c90120766f746572207768656e2063616c6c6564207769746820746865207265666572656e64756d2028796f75276c6c20676574207468652064656661756c742060566f7465602076616c7565206f7468657277697365292e20496620796f7520646f6e27742077616e7420746f20636865636b61012060766f746572735f666f72602c207468656e20796f752063616e20616c736f20636865636b20666f722073696d706c65206578697374656e636520776974682060566f74654f663a3a657869737473602066697273742e2c44656c65676174696f6e73010130543a3a4163636f756e7449646c28543a3a4163636f756e7449642c204c6f636b506572696f64732901840000000000000000000000000000000000000000000000000000000000000000000441012047657420746865206163636f756e742028616e64206c6f636b20706572696f64732920746f20776869636820616e6f74686572206163636f756e742069732064656c65676174696e6720766f74652e01201c70726f706f7365082070726f706f73616c40426f783c543a3a50726f706f73616c3e1476616c756554436f6d706163743c42616c616e63654f663c543e3e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e187365636f6e64042070726f706f73616c48436f6d706163743c50726f70496e6465783e04a02050726f706f736520612073656e73697469766520616374696f6e20746f2062652074616b656e2e10766f746508247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e10766f746510566f746508350120566f746520696e2061207265666572656e64756d2e2049662060766f74652e69735f6179652829602c2074686520766f746520697320746f20656e616374207468652070726f706f73616c3bbc206f7468657277697365206974206973206120766f746520746f206b65657020746865207374617475732071756f2e4073746172745f7265666572656e64756d0c2070726f706f73616c40426f783c543a3a50726f706f73616c3e247468726573686f6c6434566f74655468726573686f6c641464656c617938543a3a426c6f636b4e756d62657204502053746172742061207265666572656e64756d2e4463616e63656c5f7265666572656e64756d04247265665f696e64657860436f6d706163743c5265666572656e64756d496e6465783e04542052656d6f76652061207265666572656e64756d2e3463616e63656c5f71756575656408107768656e5c436f6d706163743c543a3a426c6f636b4e756d6265723e14776869636830436f6d706163743c7533323e04a02043616e63656c20612070726f706f73616c2071756575656420666f7220656e6163746d656e742e2064656c65676174650808746f30543a3a4163636f756e744964306c6f636b5f706572696f64732c4c6f636b506572696f6473043c2044656c656761746520766f74652e28756e64656c656761746500044420556e64656c656761746520766f74652e01242050726f706f736564082450726f70496e6465781c42616c616e636500185461626c65640c2450726f70496e6465781c42616c616e6365385665633c4163636f756e7449643e001c53746172746564083c5265666572656e64756d496e64657834566f74655468726573686f6c640018506173736564043c5265666572656e64756d496e64657800244e6f74506173736564043c5265666572656e64756d496e646578002443616e63656c6c6564043c5265666572656e64756d496e64657800204578656375746564083c5265666572656e64756d496e64657810626f6f6c002444656c65676174656408244163636f756e744964244163636f756e744964002c556e64656c65676174656404244163636f756e744964001c636f756e63696c1c436f756e63696c01503443616e646964616379426f6e6401003042616c616e63654f663c543e400900000000000000000000000000000004050120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f207375626d6974206f6e6527732063616e6469646163792e28566f74696e67426f6e6401003042616c616e63654f663c543e400000000000000000000000000000000004090120486f77206d7563682073686f756c64206265206c6f636b656420757020696e206f7264657220746f2062652061626c6520746f207375626d697420766f7465732e5050726573656e74536c617368506572566f74657201003042616c616e63654f663c543e4001000000000000000000000000000000040d01205468652070756e6973686d656e742c2070657220766f7465722c20696620796f752070726f7669646520616e20696e76616c69642070726573656e746174696f6e2e284361727279436f756e7401000c753332100200000004350120486f77206d616e792072756e6e6572732d75702073686f756c64206861766520746865697220617070726f76616c73207065727369737420756e74696c20746865206e65787420766f74652e5050726573656e746174696f6e4475726174696f6e010038543a3a426c6f636b4e756d62657220e803000000000000043d0120486f77206c6f6e6720746f2067697665206561636820746f702063616e64696461746520746f2070726573656e74207468656d73656c7665732061667465722074686520766f746520656e64732e4c496e6163746976654772616365506572696f64010024566f7465496e646578100100000008a10120486f77206d616e7920766f746520696e6465786573206e65656420746f20676f20627920616674657220612074617267657420766f7465722773206c61737420766f7465206265666f726520746865792063616e206265207265617065642069662074686569725020617070726f76616c7320617265206d6f6f742e30566f74696e67506572696f64010038543a3a426c6f636b4e756d62657220e80300000000000004b820486f77206f6674656e2028696e20626c6f636b732920746f20636865636b20666f72206e657720766f7465732e305465726d4475726174696f6e010038543a3a426c6f636b4e756d626572200500000000000000049820486f77206c6f6e67206561636820706f736974696f6e2069732061637469766520666f722e3044657369726564536561747301000c753332100000000004e8204e756d626572206f66206163636f756e747320746861742073686f756c642062652073697474696e67206f6e2074686520636f756e63696c2e34416374697665436f756e63696c01008c5665633c28543a3a4163636f756e7449642c20543a3a426c6f636b4e756d626572293e0400106d01205468652063757272656e7420636f756e63696c2e205768656e2074686572652773206120766f746520676f696e67206f6e2c20746869732073686f756c64207374696c6c206265207573656420666f72206578656375746976657101206d6174746572732e2054686520626c6f636b206e756d62657220287365636f6e6420656c656d656e7420696e20746865207475706c65292069732074686520626c6f636b207468617420746865697220706f736974696f6e20697371012061637469766520756e74696c202863616c63756c61746564206279207468652073756d206f662074686520626c6f636b206e756d626572207768656e2074686520636f756e63696c206d656d6265722077617320656c65637465646820616e64207468656972207465726d206475726174696f6e292e24566f7465436f756e74010024566f7465496e64657810000000000405012054686520746f74616c206e756d626572206f6620766f746573207468617420686176652068617070656e6564206f722061726520696e2070726f67726573732e2c417070726f76616c734f66010130543a3a4163636f756e744964245665633c626f6f6c3e000400086d012041206c697374206f6620766f74657320666f72206561636820766f7465722c2072657370656374696e6720746865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f7465722077617340206c617374206163746976652061742e385265676973746572496e666f4f66000130543a3a4163636f756e7449644028566f7465496e6465782c2075333229000400087d012054686520766f746520696e64657820616e64206c69737420736c6f742074686174207468652063616e646964617465206077686f60207761732072656769737465726564206f7220604e6f6e6560206966207468657920617265206e6f74582063757272656e746c7920726567697374657265642e304c6173744163746976654f66000130543a3a4163636f756e74496424566f7465496e64657800040004010120546865206c61737420636c656172656420766f746520696e6465782074686174207468697320766f74657220776173206c617374206163746976652061742e18566f746572730100445665633c543a3a4163636f756e7449643e04000460205468652070726573656e7420766f746572206c6973742e2843616e646964617465730100445665633c543a3a4163636f756e7449643e04000470205468652070726573656e742063616e646964617465206c6973742e3843616e646964617465436f756e7401000c753332100000000000304e65787446696e616c697a650000a028543a3a426c6f636b4e756d6265722c207533322c205665633c543a3a4163636f756e7449643e29040004210120546865206163636f756e747320686f6c64696e672074686520736561747320746861742077696c6c206265636f6d652066726565206f6e20746865206e6578742074616c6c792e40536e617073686f7465645374616b65730100445665633c42616c616e63654f663c543e3e040004e820546865207374616b6573206173207468657920776572652061742074686520706f696e7420746861742074686520766f746520656e6465642e2c4c6561646572626f6172640000845665633c2842616c616e63654f663c543e2c20543a3a4163636f756e744964293e040004e02047657420746865206c6561646572626f6172642069662077653b726520696e207468652070726573656e746174696f6e2070686173652e0124347365745f617070726f76616c730814766f746573245665633c626f6f6c3e14696e64657848436f6d706163743c566f7465496e6465783e086101205365742063616e64696461746520617070726f76616c732e20417070726f76616c20736c6f747320737461792076616c6964206173206c6f6e672061732063616e6469646174657320696e2074686f736520736c6f7473402061726520726567697374657265642e4c726561705f696e6163746976655f766f74657210387265706f727465725f696e64657830436f6d706163743c7533323e0c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263652477686f5f696e64657830436f6d706163743c7533323e48617373756d65645f766f74655f696e64657848436f6d706163743c566f7465496e6465783e1461012052656d6f7665206120766f7465722e20466f72206974206e6f7420746f206265206120626f6e642d636f6e73756d696e67206e6f2d6f702c20616c6c20617070726f7665642063616e64696461746520696e64696365737101206d757374206e6f772062652065697468657220756e72656769737465726564206f72207265676973746572656420746f20612063616e646964617465207468617420726567697374657265642074686520736c6f74206166746572a02074686520766f7465722067617665207468656972206c61737420617070726f76616c207365742e000101204d61792062652063616c6c656420627920616e796f6e652e2052657475726e732074686520766f746572206465706f73697420746f20607369676e6564602e34726574726163745f766f7465720414696e64657830436f6d706163743c7533323e042d012052656d6f7665206120766f7465722e20416c6c20766f746573206172652063616e63656c6c656420616e642074686520766f746572206465706f7369742069732072657475726e65642e407375626d69745f63616e6469646163790410736c6f7430436f6d706163743c7533323e0c78205375626d6974206f6e6573656c6620666f722063616e6469646163792e001101204163636f756e74206d757374206861766520656e6f756768207472616e736665727261626c652066756e647320696e20697420746f207061792074686520626f6e642e3870726573656e745f77696e6e65720c2463616e6469646174658c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f7572636514746f74616c54436f6d706163743c42616c616e63654f663c543e3e14696e64657848436f6d706163743c566f7465496e6465783e0c650120436c61696d207468617420607369676e656460206973206f6e65206f662074686520746f702053656c663a3a63617272795f636f756e742829202b2063757272656e745f766f746528292e312063616e646964617465732ea501204f6e6c7920776f726b73206966207468652060626c6f636b5f6e756d626572203e3d2063757272656e745f766f746528292e306020616e6420603c2063757272656e745f766f746528292e30202b2070726573656e746174696f6e5f6475726174696f6e282960607820607369676e6564602073686f756c642068617665206174206c65617374447365745f646573697265645f73656174730414636f756e7430436f6d706163743c7533323e0c650120536574207468652064657369726564206d656d62657220636f756e743b206966206c6f776572207468616e207468652063757272656e7420636f756e742c207468656e2073656174732077696c6c206e6f74206265207570690120656c656374696f6e207768656e2074686579206578706972652e204966206d6f72652c207468656e2061206e657720766f74652077696c6c2062652073746172746564206966206f6e65206973206e6f7420616c72656164793420696e2070726f67726573732e3472656d6f76655f6d656d626572040c77686f8c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c71012052656d6f7665206120706172746963756c6172206d656d6265722e20412074616c6c792077696c6c2068617070656e20696e7374616e746c7920286966206e6f7420616c726561647920696e20612070726573656e746174696f6e410120706572696f642920746f2066696c6c2074686520736561742069662072656d6f76616c206d65616e732074686174207468652064657369726564206d656d6265727320617265206e6f74206d65742e7c20546869732069732065666665637469766520696d6d6564696174656c792e647365745f70726573656e746174696f6e5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08590120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e746c79206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c697a655f766f7465602e447365745f7465726d5f6475726174696f6e0414636f756e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08510120536574207468652070726573656e746174696f6e206475726174696f6e2e2049662074686572652069732063757272656e74206120766f7465206265696e672070726573656e74656420666f722c2077696c6c6020696e766f6b65206066696e616c697a655f766f7465602e01102c566f74657252656170656408244163636f756e744964244163636f756e74496404542072656170656420766f7465722c2072656170657240426164526561706572536c617368656404244163636f756e744964043c20736c6173686564207265617065723054616c6c7953746172746564040c75333204f420412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320737461727465642e3854616c6c7946696e616c697a656408385665633c4163636f756e7449643e385665633c4163636f756e7449643e04690120412074616c6c792028666f7220617070726f76616c20766f746573206f6620636f756e63696c2073656174287329292068617320656e646564202877697468206f6e65206f72206d6f7265206e6577206d656d62657273292e38636f756e63696c5f766f74696e6734436f756e63696c566f74696e67012034436f6f6c6f6666506572696f64010038543a3a426c6f636b4e756d62657220e8030000000000000030566f74696e67506572696f64010038543a3a426c6f636b4e756d6265722003000000000000000040456e61637444656c6179506572696f64010038543a3a426c6f636b4e756d62657220000000000000000004cd01204e756d626572206f6620626c6f636b7320627920776869636820746f2064656c617920656e6163746d656e74206f66207375636365737366756c2c206e6f6e2d756e616e696d6f75732d636f756e63696c2d696e7374696761746564207265666572656e64756d2070726f706f73616c732e2450726f706f73616c730100785665633c28543a3a426c6f636b4e756d6265722c20543a3a48617368293e0400002850726f706f73616c4f6600011c543a3a486173682c543a3a50726f706f73616c000400003850726f706f73616c566f7465727301011c543a3a48617368445665633c543a3a4163636f756e7449643e0004000034436f756e63696c566f74654f6600015c28543a3a486173682c20543a3a4163636f756e7449642910626f6f6c00040000385665746f656450726f706f73616c00011c543a3a486173688c28543a3a426c6f636b4e756d6265722c205665633c543a3a4163636f756e7449643e290004000001141c70726f706f7365042070726f706f73616c40426f783c543a3a50726f706f73616c3e0010766f7465082070726f706f73616c1c543a3a486173681c617070726f766510626f6f6c00107665746f043470726f706f73616c5f686173681c543a3a4861736800487365745f636f6f6c6f66665f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e00447365745f766f74696e675f706572696f640418626c6f636b735c436f6d706163743c543a3a426c6f636b4e756d6265723e0001084054616c6c7943616e63656c6174696f6e1010486173680c7533320c7533320c753332080101204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d2063616e63656c6c6174696f6e20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c54616c6c795265666572656e64756d1010486173680c7533320c7533320c75333208cc204120766f74696e672074616c6c79206861732068617070656e656420666f722061207265666572656e64756d20766f74652ea0204c61737420746872656520617265207965732c206e6f2c206162737461696e20636f756e74732e3c636f756e63696c5f6d6f74696f6e7338436f756e63696c4d6f74696f6e7301102450726f706f73616c730100305665633c543a3a486173683e04000498205468652028686173686573206f662920746865206163746976652070726f706f73616c732e2850726f706f73616c4f6600011c543a3a48617368583c542061732054726169743e3a3a50726f706f73616c00040004cc2041637475616c2070726f706f73616c20666f72206120676976656e20686173682c20696620697427732063757272656e742e18566f74696e6700011c543a3a48617368e82850726f706f73616c496e6465782c207533322c205665633c543a3a4163636f756e7449643e2c205665633c543a3a4163636f756e7449643e2900040004250120566f74657320666f72206120676976656e2070726f706f73616c3a202872657175697265645f7965735f766f7465732c207965735f766f746572732c206e6f5f766f74657273292e3450726f706f73616c436f756e7401000c753332100000000004482050726f706f73616c7320736f206661722e01081c70726f706f736508247468726573686f6c6430436f6d706163743c7533323e2070726f706f73616c6c426f783c3c542061732054726169743e3a3a50726f706f73616c3e0010766f74650c2070726f706f73616c1c543a3a4861736814696e64657858436f6d706163743c50726f706f73616c496e6465783e1c617070726f766510626f6f6c0001142050726f706f73656410244163636f756e7449643450726f706f73616c496e64657810486173680c7533320465012041206d6f74696f6e2028676976656e20686173682920686173206265656e2070726f706f7365642028627920676976656e206163636f756e742920776974682061207468726573686f6c642028676976656e20753332292e14566f74656414244163636f756e744964104861736810626f6f6c0c7533320c7533320809012041206d6f74696f6e2028676976656e20686173682920686173206265656e20766f746564206f6e20627920676976656e206163636f756e742c206c656176696e67f420612074616c6c79202879657320766f74657320616e64206e6f20766f74657320676976656e206173207533327320726573706563746976656c79292e20417070726f76656404104861736804c42041206d6f74696f6e2077617320617070726f76656420627920746865207265717569726564207468726573686f6c642e2c446973617070726f76656404104861736804d42041206d6f74696f6e20776173206e6f7420617070726f76656420627920746865207265717569726564207468726573686f6c642e20457865637574656408104861736810626f6f6c0405012041206d6f74696f6e207761732065786563757465643b2060626f6f6c6020697320747275652069662072657475726e656420776974686f7574206572726f722e4066696e616c6974795f747261636b6572000001042866696e616c5f68696e74041068696e745c436f6d706163743c543a3a426c6f636b4e756d6265723e08f42048696e7420746861742074686520617574686f72206f66207468697320626c6f636b207468696e6b732074686520626573742066696e616c697a65646c20626c6f636b2069732074686520676976656e206e756d6265722e001c6772616e6470613c4772616e64706146696e616c69747901083450656e64696e674368616e67650000c853746f72656450656e64696e674368616e67653c543a3a426c6f636b4e756d6265722c20543a3a53657373696f6e4b65793e040000284e657874466f72636564000038543a3a426c6f636b4e756d6265720400000104487265706f72745f6d69736265686176696f72041c5f7265706f72741c5665633c75383e0464205265706f727420736f6d65206d69736265686176696f722e0104384e6577417574686f72697469657304585665633c2853657373696f6e4b65792c20753634293e0490204e657720617574686f726974792073657420686173206265656e206170706c6965642e20747265617375727920547265617375727901203050726f706f73616c426f6e6401001c5065726d696c6c10000000000851012050726f706f7274696f6e206f662066756e647320746861742073686f756c6420626520626f6e64656420696e206f7264657220746f20706c61636520612070726f706f73616c2e20416e206163636570746564dc2070726f706f73616c2067657473207468657365206261636b2e20412072656a65637465642070726f706f73616c20646f65736e27742e4c50726f706f73616c426f6e644d696e696d756d01003042616c616e63654f663c543e4000000000000000000000000000000000044901204d696e696d756d20616d6f756e74206f662066756e647320746861742073686f756c6420626520706c6163656420696e2061206465706f73697420666f72206d616b696e6720612070726f706f73616c2e2c5370656e64506572696f64010038543a3a426c6f636b4e756d626572200100000000000000048820506572696f64206265747765656e2073756363657373697665207370656e64732e104275726e01001c5065726d696c6c10000000000411012050657263656e74616765206f662073706172652066756e64732028696620616e7929207468617420617265206275726e7420706572207370656e6420706572696f642e0c506f7401003042616c616e63654f663c543e400000000000000000000000000000000004cc20546f74616c2066756e647320617661696c61626c6520746f2074686973206d6f64756c6520666f72207370656e64696e672e3450726f706f73616c436f756e7401003450726f706f73616c496e646578100000000004a4204e756d626572206f662070726f706f73616c7320746861742068617665206265656e206d6164652e2450726f706f73616c7300013450726f706f73616c496e6465789050726f706f73616c3c543a3a4163636f756e7449642c2042616c616e63654f663c543e3e000400047c2050726f706f73616c7320746861742068617665206265656e206d6164652e24417070726f76616c730100485665633c50726f706f73616c496e6465783e040004f82050726f706f73616c20696e646963657320746861742068617665206265656e20617070726f76656420627574206e6f742079657420617761726465642e01143470726f706f73655f7370656e64081476616c756554436f6d706163743c42616c616e63654f663c543e3e2c62656e65666963696172798c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650c2d012050757420666f727761726420612073756767657374696f6e20666f72207370656e64696e672e2041206465706f7369742070726f706f7274696f6e616c20746f207468652076616c7565350120697320726573657276656420616e6420736c6173686564206966207468652070726f706f73616c2069732072656a65637465642e2049742069732072657475726e6564206f6e636520746865542070726f706f73616c20697320617761726465642e1c7365745f706f74041c6e65775f706f7454436f6d706163743c42616c616e63654f663c543e3e04b420536574207468652062616c616e6365206f662066756e647320617661696c61626c6520746f207370656e642e24636f6e666967757265103470726f706f73616c5f626f6e6440436f6d706163743c5065726d696c6c3e5470726f706f73616c5f626f6e645f6d696e696d756d54436f6d706163743c42616c616e63654f663c543e3e307370656e645f706572696f645c436f6d706163743c543a3a426c6f636b4e756d6265723e106275726e40436f6d706163743c5065726d696c6c3e0470202852652d29636f6e6669677572652074686973206d6f64756c652e3c72656a6563745f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e04fc2052656a65637420612070726f706f736564207370656e642e20546865206f726967696e616c206465706f7369742077696c6c20626520736c61736865642e40617070726f76655f70726f706f73616c042c70726f706f73616c5f696458436f6d706163743c50726f706f73616c496e6465783e085d0120417070726f766520612070726f706f73616c2e2041742061206c617465722074696d652c207468652070726f706f73616c2077696c6c20626520616c6c6f636174656420746f207468652062656e6566696369617279ac20616e6420746865206f726967696e616c206465706f7369742077696c6c2062652072657475726e65642e01142050726f706f736564043450726f706f73616c496e6465780438204e65772070726f706f73616c2e205370656e64696e67041c42616c616e636504e8205765206861766520656e6465642061207370656e6420706572696f6420616e642077696c6c206e6f7720616c6c6f636174652066756e64732e1c417761726465640c3450726f706f73616c496e6465781c42616c616e6365244163636f756e744964048020536f6d652066756e64732068617665206265656e20616c6c6f63617465642e144275726e74041c42616c616e6365048c20536f6d65206f66206f75722066756e64732068617665206265656e206275726e742e20526f6c6c6f766572041c42616c616e6365043101205370656e64696e67206861732066696e69736865643b20746869732069732074686520616d6f756e74207468617420726f6c6c73206f76657220756e74696c206e657874207370656e642e20636f6e747261637420436f6e747261637401442c5472616e7366657246656501003042616c616e63654f663c543e40000000000000000000000000000000000494205468652066656520726571756972656420746f206d616b652061207472616e736665722e2c4372656174696f6e46656501003042616c616e63654f663c543e4000000000000000000000000000000000049c205468652066656520726571756972656420746f2063726561746520616e206163636f756e742e485472616e73616374696f6e4261736546656501003042616c616e63654f663c543e400000000000000000000000000000000004dc205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b2074686520626173652e485472616e73616374696f6e4279746546656501003042616c616e63654f663c543e4000000000000000000000000000000000040d01205468652066656520746f206265207061696420666f72206d616b696e672061207472616e73616374696f6e3b20746865207065722d6279746520706f7274696f6e2e2c436f6e747261637446656501003042616c616e63654f663c543e401500000000000000000000000000000004c0205468652066656520726571756972656420746f20637265617465206120636f6e747261637420696e7374616e63652e2c43616c6c42617365466565010018543a3a47617320870000000000000004c820546865206261736520666565206368617267656420666f722063616c6c696e6720696e746f206120636f6e74726163742e3443726561746542617365466565010018543a3a47617320af0000000000000004b820546865206261736520666565206368617267656420666f72206372656174696e67206120636f6e74726163742e20476173507269636501003042616c616e63654f663c543e4001000000000000000000000000000000047820546865207072696365206f66206f6e6520756e6974206f66206761732e204d6178446570746801000c753332106400000004c820546865206d6178696d756d206e657374696e67206c6576656c206f6620612063616c6c2f63726561746520737461636b2e34426c6f636b4761734c696d6974010018543a3a4761732040420f000000000004f020546865206d6178696d756d20616d6f756e74206f6620676173207468617420636f756c6420626520657870656e6465642070657220626c6f636b2e204761735370656e74010018543a3a476173200000000000000000048020476173207370656e7420736f2066617220696e207468697320626c6f636b2e3c43757272656e745363686564756c650100405363686564756c653c543a3a4761733ef000000000010000000000000001000000000000000100000000000000010000000000000001000000000000000100000000000000000001001000000004942043757272656e7420636f7374207363686564756c6520666f7220636f6e7472616374732e28436f6465486173684f66000130543a3a4163636f756e7449642c436f6465486173683c543e00040004a82054686520636f6465206173736f6369617465642077697468206120676976656e206163636f756e742e305072697374696e65436f646500012c436f6465486173683c543e1c5665633c75383e0004000465012041206d617070696e672066726f6d20616e206f726967696e616c20636f6465206861736820746f20746865206f726967696e616c20636f64652c20756e746f756368656420627920696e737472756d656e746174696f6e2e2c436f646553746f7261676500012c436f6465486173683c543e587761736d3a3a5072656661625761736d4d6f64756c650004000475012041206d617070696e67206265747765656e20616e206f726967696e616c20636f6465206861736820616e6420696e737472756d656e746564207761736d20636f64652c20726561647920666f722074686520657865637574696f6e2e384163636f756e74436f756e74657201000c753634200000000000000000045020546865207375627472696520636f756e746572344163636f756e74496e666f4f66000130543a3a4163636f756e7449642c4163636f756e74496e666f00040004a82054686520636f6465206173736f6369617465642077697468206120676976656e206163636f756e742e01103c7570646174655f7363686564756c6504207363686564756c65405363686564756c653c543a3a4761733e0cb4205570646174657320746865207363686564756c6520666f72206d65746572696e6720636f6e7472616374732e000d0120546865207363686564756c65206d7573742068617665206120677265617465722076657273696f6e207468616e207468652073746f726564207363686564756c652e207075745f636f646508246761735f6c696d69743c436f6d706163743c543a3a4761733e10636f64651c5665633c75383e0859012053746f7265732074686520676976656e2062696e617279205761736d20636f646520696e746f2074686520636861696e732073746f7261676520616e642072657475726e73206974732060636f646568617368602ed420596f752063616e20696e7374616e746961746520636f6e747261637473206f6e6c7920776974682073746f72656420636f64652e1063616c6c1010646573748c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263651476616c756554436f6d706163743c42616c616e63654f663c543e3e246761735f6c696d69743c436f6d706163743c543a3a4761733e10646174611c5665633c75383e1c0901204d616b657320612063616c6c20746f20616e206163636f756e742c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e002901202a20496620746865206163636f756e74206973206120736d6172742d636f6e7472616374206163636f756e742c20746865206173736f63696174656420636f64652077696c6c206265b020657865637574656420616e6420616e792076616c75652077696c6c206265207472616e736665727265642e1901202a20496620746865206163636f756e74206973206120726567756c6172206163636f756e742c20616e792076616c75652077696c6c206265207472616e736665727265642e4901202a204966206e6f206163636f756e742065786973747320616e64207468652063616c6c2076616c7565206973206e6f74206c657373207468616e20606578697374656e7469616c5f6465706f736974602c1501206120726567756c6172206163636f756e742077696c6c206265206372656174656420616e6420616e792076616c75652077696c6c206265207472616e736665727265642e186372656174651024656e646f776d656e7454436f6d706163743c42616c616e63654f663c543e3e246761735f6c696d69743c436f6d706163743c543a3a4761733e24636f64655f686173682c436f6465486173683c543e10646174611c5665633c75383e28a90120437265617465732061206e657720636f6e74726163742066726f6d207468652060636f646568617368602067656e65726174656420627920607075745f636f6465602c206f7074696f6e616c6c79207472616e7366657272696e6720736f6d652062616c616e63652e0084204372656174696f6e20697320657865637574656420617320666f6c6c6f77733a004101202d207468652064657374696e6174696f6e206164647265737320697320636f6d7075746564206261736564206f6e207468652073656e64657220616e642068617368206f662074686520636f64652e0501202d2074686520736d6172742d636f6e7472616374206163636f756e7420697320637265617465642061742074686520636f6d707574656420616464726573732e6d01202d20746865206063746f725f636f64656020697320657865637574656420696e2074686520636f6e74657874206f6620746865206e65776c792063726561746564206163636f756e742e204275666665722072657475726e65645d0120202061667465722074686520657865637574696f6e206973207361766564206173207468652060636f646560206f6620746865206163636f756e742e205468617420636f64652077696c6c20626520696e766f6b6564a820202075706f6e20616e792063616c6c2072656365697665642062792074686973206163636f756e742e7c202d2054686520636f6e747261637420697320696e697469616c697a65642e0114205472616e736665720c244163636f756e744964244163636f756e7449641c42616c616e6365045501205472616e736665722068617070656e6564206066726f6d6020746f2060746f60207769746820676976656e206076616c7565602061732070617274206f662061206063616c6c60206f722060637265617465602e30496e7374616e74696174656408244163636f756e744964244163636f756e74496404dc20436f6e7472616374206465706c6f7965642062792061646472657373206174207468652073706563696669656420616464726573732e28436f646553746f72656404104861736804b820436f646520776974682074686520737065636966696564206861736820686173206265656e2073746f7265642e3c5363686564756c6555706461746564040c75333204c020547269676765726564207768656e207468652063757272656e74207363686564756c6520697320757064617465642e284469737061746368656408244163636f756e74496410626f6f6c08390120412063616c6c2077617320646973706174636865642066726f6d2074686520676976656e206163636f756e742e2054686520626f6f6c207369676e616c7320776865746865722069742077617374207375636365737366756c20657865637574696f6e206f72206e6f742e107375646f105375646f01040c4b6579010030543a3a4163636f756e744964800000000000000000000000000000000000000000000000000000000000000000000108107375646f042070726f706f73616c40426f783c543a3a50726f706f73616c3e001c7365745f6b6579040c6e65778c3c543a3a4c6f6f6b7570206173205374617469634c6f6f6b75703e3a3a536f757263650001081453756469640410626f6f6c04602041207375646f206a75737420746f6f6b20706c6163652e284b65794368616e67656404244163636f756e74496404f020546865207375646f6572206a757374207377697463686564206964656e746974793b20746865206f6c64206b657920697320737570706c6965642e'; + +export default meta; diff --git a/packages/types/src/Metadata/v3/toV2.ts b/packages/types/src/Metadata/v3/toV2.ts new file mode 100644 index 0000000000..3b2c4fc380 --- /dev/null +++ b/packages/types/src/Metadata/v3/toV2.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import MetadataV2 from '../v2/Metadata'; +import MetadataV3 from '.'; + +export default function toV2 (v2: MetadataV3): MetadataV2 { + return new MetadataV2(v2); +} diff --git a/packages/types/src/classes.ts b/packages/types/src/classes.ts new file mode 100644 index 0000000000..47ddb9cc97 --- /dev/null +++ b/packages/types/src/classes.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import * as Classes from './index.types'; + +export type CodecTypes = keyof typeof Classes; diff --git a/packages/types/src/codec/AbstractInt.ts b/packages/types/src/codec/AbstractInt.ts new file mode 100644 index 0000000000..fa77d1c2c5 --- /dev/null +++ b/packages/types/src/codec/AbstractInt.ts @@ -0,0 +1,131 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber, Codec } from '../types'; + +import BN from 'bn.js'; +import { bnToBn, hexToBn, isHex, isString, isU8a, u8aToBn } from '@plugnet/util'; + +export type UIntBitLength = 8 | 16 | 32 | 64 | 128 | 256; + +export const DEFAULT_UINT_BITS = 64; + +/** + * @name AbstractInt + * @ignore + * @noInheritDoc + */ +// TODO: +// - Apart from encoding/decoding we don't actually keep check on the sizes, is this good enough? +export default abstract class AbstractInt extends BN implements Codec { + protected _bitLength: UIntBitLength; + private _isHexJson: boolean; + private _isNegative: boolean; + + constructor ( + isNegative: boolean, + value: AnyNumber = 0, + bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = true) { + super( + AbstractInt.decodeAbstracInt(value, bitLength, isNegative) + ); + + this._bitLength = bitLength; + this._isHexJson = isHexJson; + this._isNegative = isNegative; + } + + static decodeAbstracInt (value: AnyNumber, bitLength: UIntBitLength, isNegative: boolean): string { + // This function returns a string, which will be passed in the BN + // constructor. It would be ideal to actually return a BN, but there's a + // bug: https://github.com/indutny/bn.js/issues/206. + if (isHex(value)) { + return hexToBn(value, { isLe: false, isNegative }).toString(); + } else if (isU8a(value)) { + try { + // NOTE When passing u8a in (typically from decoded data), it is always LE + return u8aToBn(value.subarray(0, bitLength / 8), { isLe: true, isNegative }).toString(); + } catch (error) { + console.error(`AbstractInt value decoding failed ${error.message}`, value); + + return '0'; + } + } else if (isString(value)) { + return new BN(value, 10).toString(); + } + + return bnToBn(value).toString(); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this._bitLength / 8; + } + + /** + * @description Checks if the value is a zero value (align elsewhere) + */ + get isEmpty (): boolean { + return this.isZero(); + } + + /** + * @description Returns the number of bits in the value + */ + bitLength (): UIntBitLength { + return this._bitLength; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + // Here we are actually overriding the built-in .eq to take care of both + // number and BN inputs (no `.eqn` needed) - numbers will be converted + return super.eq( + isHex(other) + ? hexToBn(other.toString(), { isLe: false, isNegative: this._isNegative }) + : bnToBn(other) + ); + } + + /** + * @description Returns the BN representation of the number. (Compatibility) + */ + toBn (): BN { + return this; + } + + /** + * @description Returns a hex string representation of the value + */ + abstract toHex (): string; + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + // Maximum allowed integer for JS is 2^53 - 1, set limit at 52 + return this._isHexJson || (super.bitLength() > 52) + ? this.toHex() + : this.toNumber(); + } + + /** + * @description Returns the string representation of the value + * @param base The base to use for the conversion + */ + toString (base?: number): string { + // only included here since we do not inherit docs + return super.toString(base); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + abstract toU8a (isBare?: boolean): Uint8Array; +} diff --git a/packages/types/src/codec/Base.ts b/packages/types/src/codec/Base.ts new file mode 100644 index 0000000000..977830932a --- /dev/null +++ b/packages/types/src/codec/Base.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name Base + * @description A type extends the Base class, when it holds a value + */ +export default class Base { + protected raw: T; + + constructor (value?: any) { + this.raw = value; + } +} diff --git a/packages/types/src/codec/Compact.spec.ts b/packages/types/src/codec/Compact.spec.ts new file mode 100644 index 0000000000..08b9178c31 --- /dev/null +++ b/packages/types/src/codec/Compact.spec.ts @@ -0,0 +1,161 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import Moment from '../primitive/Moment'; +import U32 from '../primitive/U32'; +import Balance from '../type/Balance'; +import BlockNumber from '../type/BlockNumber'; +import Compact from './Compact'; +import UInt from './UInt'; + +describe('Compact', () => { + describe('encodeU8a', () => { + it('encodes short u8', () => { + expect( + Compact.encodeU8a(18) + ).toEqual( + new Uint8Array([18 << 2]) + ); + }); + + it('encodes max u8 values', () => { + expect( + Compact.encodeU8a(new UInt(63)) + ).toEqual( + new Uint8Array([0b11111100]) + ); + }); + + it('encodes basic u16 value', () => { + expect( + Compact.encodeU8a(511) + ).toEqual( + new Uint8Array([0b11111101, 0b00000111]) + ); + }); + + it('encodes basic ua6 (not at edge)', () => { + expect( + Compact.encodeU8a(111) + ).toEqual( + new Uint8Array([0xbd, 0x01]) + ); + }); + + it('encodes basic u32 values (short)', () => { + expect( + Compact.encodeU8a(0xffff) + ).toEqual( + new Uint8Array([254, 255, 3, 0]) + ); + }); + + it('encodes basic u32 values (full)', () => { + expect( + Compact.encodeU8a(0xfffffff9) + ).toEqual( + new Uint8Array([3, 249, 255, 255, 255]) + ); + }); + + it('encondes a large balance', () => { + expect( + Compact.encodeU8a(new Balance('0x5af3107a4000')) + ).toEqual( + new Uint8Array([ + 3 + ((6 - 4) << 2), + 0x00, 0x40, 0x7a, 0x10, 0xf3, 0x5a + ]) + ); + }); + }); + + describe('decodeU8a', () => { + it('decoded u8 value', () => { + expect( + Compact.decodeU8a(new Uint8Array([0b11111100]), 32) + ).toEqual([1, new BN(63)]); + }); + + it('decodes from same u16 encoded value', () => { + expect( + Compact.decodeU8a(new Uint8Array([0b11111101, 0b00000111]), 32) + ).toEqual([2, new BN(511)]); + }); + + it('decodes from same u32 encoded value (short)', () => { + expect( + Compact.decodeU8a(new Uint8Array([254, 255, 3, 0]), 32) + ).toEqual([4, new BN(0xffff)]); + }); + + it('decodes from same u32 encoded value (full)', () => { + expect( + Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 32) + ).toEqual([5, new BN(0xfffffff9)]); + }); + + it('decodes from same u32 as u64 encoded value (full, default)', () => { + expect( + Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 64) + ).toEqual([5, new BN(0xfffffff9)]); + }); + }); + + describe('constructor', () => { + it('has the correct bitLength for constructor values (BlockNumber)', () => { + expect( + new Compact(BlockNumber, 0xfffffff9).bitLength() + ).toEqual(64); + }); + + it('has the correct encodedLength for constructor values (BlockNumber)', () => { + expect( + new Compact(BlockNumber, 0xfffffff9).encodedLength + ).toEqual(5); + }); + + it('has the correct encodedLength for constructor values (u32)', () => { + expect( + new Compact(U32, 0xffff9).encodedLength + ).toEqual(4); + }); + + it('constructs properly via U8a as U32', () => { + expect( + new Compact(U32, new Uint8Array([254, 255, 3, 0])).toNumber() + ).toEqual(new BN(0xffff).toNumber()); + }); + + it('constructs properly via number as Moment', () => { + expect( + new Compact(Moment, 1537968546).toString().startsWith('Wed Sep 26 2018') // The time depends on the timezone this test is run in + ).toBe(true); + }); + }); + + describe('utils', () => { + it('compares against another Compact', () => { + expect(new Compact(U32, 12345).eq(new Compact(U32, 12345))).toBe(true); + }); + + it('compares against a primitive', () => { + expect(new Compact(U32, 12345).eq(12345)).toBe(true); + }); + + it('unwraps to the wrapped value', () => { + expect(new Compact(U32, 12345).unwrap() instanceof U32).toBe(true); + }); + }); + + describe('helpers', () => { + it('correctly adds the length prefix', () => { + expect( + Compact.addLengthPrefix(Uint8Array.from([12, 13])) + ).toEqual(Uint8Array.from([2 << 2, 12, 13])); + }); + }); +}); diff --git a/packages/types/src/codec/Compact.ts b/packages/types/src/codec/Compact.ts new file mode 100644 index 0000000000..22c3016623 --- /dev/null +++ b/packages/types/src/codec/Compact.ts @@ -0,0 +1,150 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { bnToBn, compactAddLength, compactFromU8a, compactStripLength, compactToU8a, hexToBn, isBn, isHex, isNumber, isString } from '@plugnet/util'; +import { DEFAULT_BITLENGTH } from '@plugnet/util/compact/defaults'; + +import Moment from '../primitive/Moment'; +import { AnyNumber, Codec, Constructor } from '../types'; +import { UIntBitLength } from './AbstractInt'; +import Base from './Base'; +import UInt from './UInt'; + +/** + * @name Compact + * @description + * A compact length-encoding codec wrapper. It performs the same function as Length, however + * differs in that it uses a variable number of bytes to do the actual encoding. This is mostly + * used by other types to add length-prefixed encoding, or in the case of wrapped types, taking + * a number and making the compact representation thereof + */ +export default class Compact extends Base implements Codec { + constructor (Type: Constructor, value: Compact | AnyNumber = 0) { + super(Compact.decodeCompact(Type, value)); + } + + static with (Type: Constructor): Constructor { + return class extends Compact { + constructor (value?: any) { + super(Type, value); + } + }; + } + + /** + * Prepend a Uint8Array with its compact length. + * + * @param u8a - The Uint8Array to be prefixed + */ + static addLengthPrefix = compactAddLength; + static decodeU8a = compactFromU8a; + static encodeU8a = compactToU8a; + + static stripLengthPrefix (u8a: Uint8Array, bitLength: UIntBitLength = DEFAULT_BITLENGTH): Uint8Array { + const [, value] = compactStripLength(u8a, bitLength); + + return value; + } + + static decodeCompact (Type: Constructor, value: Compact | AnyNumber): Moment | UInt { + if (value instanceof Compact) { + return new Type(value.raw); + } else if (isString(value)) { + return new Type( + isHex(value, -1, true) + ? hexToBn(value) + : new BN(value, 10) + ); + } else if (isNumber(value) || isBn(value)) { + return new Type(bnToBn(value)); + } + + const [, _value] = Compact.decodeU8a(value, new Type(0).bitLength()); + + return new Type(_value); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.toU8a().length; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.raw.isEmpty; + } + + /** + * @description Returns the number of bits in the value + */ + bitLength (): UIntBitLength { + return this.raw.bitLength(); + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return this.raw.eq( + other instanceof Compact + ? other.raw + : other + ); + } + + /** + * @description Returns the BN representation of the number + */ + toBn (): BN { + return this.raw.toBn(); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): any { + return this.raw.toHex(); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.raw.toJSON(); + } + + /** + * @description Returns the number representation for the value + */ + toNumber (): number { + return this.raw.toNumber(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.raw.toString(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return Compact.encodeU8a(this.raw.toBn()); + } + + /** + * @description Returns the embedded [[UInt]] or [[Moment]] value + */ + unwrap (): UInt | Moment { + return this.raw; + } +} diff --git a/packages/types/src/codec/Enum.spec.ts b/packages/types/src/codec/Enum.spec.ts new file mode 100644 index 0000000000..d5a251ad4d --- /dev/null +++ b/packages/types/src/codec/Enum.spec.ts @@ -0,0 +1,83 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Enum from './Enum'; + +const testDecode = (type: string, input: any, expected: any) => + it(`can decode from ${type}`, () => { + const e = new Enum(['foo', 'bar'], input); + expect(e.toString()).toBe(expected); + }); + +const testEncode = (to: 'toJSON' | 'toNumber' | 'toString' | 'toU8a', expected: any) => + it(`can encode ${to}`, () => { + const e = new Enum(['foo', 'bar'], 1); + + expect(e[to]()).toEqual(expected); + }); + +describe('Enum', () => { + testDecode('Enum', undefined, 'foo'); + testDecode('Enum', new Enum([], 1), 'bar'); + testDecode('number', 0, 'foo'); + testDecode('number', 1, 'bar'); + testDecode('string', 'bar', 'bar'); + testDecode('Uint8Array', Uint8Array.from([0]), 'foo'); + testDecode('Uint8Array', Uint8Array.from([1]), 'bar'); + + testEncode('toJSON', 1); + testEncode('toNumber', 1); + testEncode('toString', 'bar'); + testEncode('toU8a', Uint8Array.from([1])); + + it('provides a clean toString()', () => { + expect( + new Enum(['foo', 'bar']).toString() + ).toEqual('foo'); + }); + + it('provides a clean toString() (enum)', () => { + expect( + new Enum(['foo', 'bar'], new Enum([], 1)).toNumber() + ).toEqual(1); + }); + + it('converts to and from U8a', () => { + expect( + new Enum(['foo', 'bar'], new Uint8Array([1])).toU8a() + ).toEqual(new Uint8Array([1])); + }); + + it('converts from JSON', () => { + expect( + new Enum(['foo', 'bar'], 5).toString() + ).toEqual('5'); + }); + + describe('utils', () => { + it('compares agains the index value', () => { + expect( + new Enum(['foo', 'bar'], 1).eq(1) + ).toBe(true); + }); + + it('compares agains the index value (false)', () => { + expect( + new Enum(['foo', 'bar'], 1).eq(0) + ).toBe(false); + }); + + it('compares agains the string value', () => { + expect( + new Enum(['foo', 'bar'], 1).eq('bar') + ).toBe(true); + }); + + it('compares agains the string value (false)', () => { + expect( + new Enum(['foo', 'bar'], 1).eq('foo') + ).toBe(false); + }); + }); +}); diff --git a/packages/types/src/codec/Enum.ts b/packages/types/src/codec/Enum.ts new file mode 100644 index 0000000000..693799cf71 --- /dev/null +++ b/packages/types/src/codec/Enum.ts @@ -0,0 +1,130 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isString, isU8a, u8aToHex, assert } from '@plugnet/util'; + +import { Codec } from '../types'; +import Base from './Base'; + +type EnumMap = { + [index: number]: string +}; + +type EnumDef = { + [index: string]: number +} | Array; + +/** + * @name Enum + * @description + * A codec wrapper for an enum. Enums are encoded as a single byte, where the byte + * is a zero-indexed value. This class allows you to retrieve the value either + * by `toNumber()` exposing the actual raw index, or `toString()` returning a + * string representation (as provided as part of the constructor) + */ +// TODO: +// - It would be great if this could actually wrap actual TS enums +export default class Enum extends Base implements Codec { + private _enum: EnumMap | Array; + + constructor (def: EnumDef, value: Enum | Uint8Array | string | number = 0) { + const decoded = Enum.decodeEnum(def, value); + + assert(decoded !== -1, `Unable to initialise Enum with value ${value}`); + + super(decoded); + + this._enum = Array.isArray(def) + ? def + : Object.keys(def).reduce((result, key) => { + result[def[key]] = key; + + return result; + }, {} as EnumMap); + } + + static decodeEnum (def: EnumDef, value: Enum | Uint8Array | string | number): number | undefined { + if (value instanceof Enum) { + return value.raw; + } else if (isU8a(value)) { + return value[0]; + } else if (isString(value)) { + return Array.isArray(def) + ? def.indexOf(value) + : def[value] || -1; + } + + return value; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return 1; + } + + /** + * @description Returns the index for this value (equivalent to toNumber) + */ + get index (): number { + return this.raw; + } + + /** + * @description Checks if the value is an empty value (always false) + */ + get isEmpty (): boolean { + return false; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + if (other instanceof Enum) { + return this.raw === other.raw; + } + + return isString(other) + ? this.toString() === other + : this.raw === other; + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.raw; + } + + /** + * @description Returns the number representation for the value + */ + toNumber (): number { + return this.raw; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this._enum[this.raw] || `${this.raw}`; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return new Uint8Array([this.raw]); + } +} diff --git a/packages/types/src/codec/EnumType.spec.ts b/packages/types/src/codec/EnumType.spec.ts new file mode 100644 index 0000000000..7330fc32e8 --- /dev/null +++ b/packages/types/src/codec/EnumType.spec.ts @@ -0,0 +1,133 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from './EnumType'; +import Null from '../primitive/Null'; +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; + +describe('Struct', () => { + it('provides a clean toString() (value)', () => { + expect( + new EnumType( + { Text, U32 }, + new Uint8Array([0, 2 << 2, 49, 50]) + ).value.toString() + ).toEqual('12'); + }); + + it('provides a clean toString() (enum)', () => { + expect( + new EnumType( + { Text, U32 }, + new Uint8Array([1, 2 << 2, 49, 50]) + ).toString() + ).toEqual('{"U32":3289352}'); + }); + + it('decodes from a JSON input (lowercase)', () => { + expect( + new EnumType( + { Text, U32 }, + { 'text': 'some text value' } + ).value.toString() + ).toEqual('some text value'); + }); + + it('decodes from hex', () => { + expect( + new EnumType( + { Text, U32 }, + '0x0134120000' + ).value.toString() + ).toEqual('4660'); // 0x1234 in decimal + }); + + it('decodes from a JSON input (mixed case)', () => { + expect( + new EnumType( + { Text, U32 }, + { 'U32': 42 } + ).value.toString() + ).toEqual('42'); + }); + + it('decodes from JSON string', () => { + expect( + new EnumType( + { Null, U32 }, + 'null' + ).type + ).toEqual('Null'); + }); + + it('stringifies with custom types', () => { + class A extends Null { } + class B extends Null { } + class C extends Null { } + class Test extends EnumType { + constructor (value?: string, index?: number) { + super({ + a: A, + b: B, + c: C + }, value, index); + } + } + + expect(new Test().toJSON()).toEqual({ a: null }); + }); + + it('creates via with', () => { + class A extends Null { } + class B extends U32 { } + class C extends Null { } + const Test = EnumType.with({ A, B, C }); + + expect(new Test().toJSON()).toEqual({ A: null }); + expect(new Test(1234, 1).toJSON()).toEqual({ B: 1234 }); + }); + + // We are currently not using this approach, none of the types in Substrate currently + // have any overrides. Insteda of trying to support it (just-in-case), rather have it + // removed to simplyfy the code - it can be pulled-back if needed + it.skip('allows checking against defined indexes', () => { + expect( + new EnumType( + { 1: Text, 5: U32 }, + new Uint8Array([1, 2 << 2, 49, 50]) + ).toString() + ).toEqual('Text'); + }); + + it('allows accessing the type and value', () => { + const text = new Text('foo'); + const enumType = new EnumType( + { Text, U32 }, + { Text: text } + ); + + expect(enumType.type).toBe('Text'); + expect(enumType.value).toEqual(text); + }); + + describe('utils', () => { + const eqtest = new EnumType( + { U32, Text }, + new Uint8Array([1, 3 << 2, 88, 89, 90]) + ); + + it('compares against index', () => { + expect( + eqtest.eq(1) + ).toBe(true); + }); + + it('compares against values', () => { + expect( + eqtest.eq('XYZ') + ).toBe(true); + }); + }); +}); diff --git a/packages/types/src/codec/EnumType.ts b/packages/types/src/codec/EnumType.ts new file mode 100644 index 0000000000..abd331db60 --- /dev/null +++ b/packages/types/src/codec/EnumType.ts @@ -0,0 +1,229 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec, Constructor, ConstructorDef } from '../types'; + +import { assert, hexToU8a, isHex, isNumber, isObject, isString, isU8a, u8aConcat, u8aToHex } from '@plugnet/util'; + +import Null from '../primitive/Null'; +import Base from './Base'; + +type EnumConstructor = { new(value?: any, index?: number): T }; + +type TypesDef = { + [name: string]: Constructor +}; + +type Decoded = { + index: number, + value: Codec +}; + +type Aliasses = { + [index: string]: string +}; + +/** + * @name EnumType + * @description + * This implements an enum, that based on the value wraps a different type. It is effectively + * an extension to enum where the value type is determined by the actual index. + */ +// TODO: +// - As per Enum, actually use TS enum +// - It should rather probably extend Enum instead of copying code +export default class EnumType extends Base implements Codec { + private _def: TypesDef; + private _index: number; + private _indexes: Array; + + constructor (def: TypesDef, value?: any, index?: number | EnumType, aliasses?: Aliasses) { + const decoded = EnumType.decodeEnumType(def, aliasses || {}, value, index); + + super(decoded.value); + + this._def = def; + this._indexes = Object.keys(def).map((_, index) => index); + this._index = this._indexes.indexOf(decoded.index) || 0; + } + + private static decodeEnumType (def: TypesDef, aliasses: Aliasses, value?: any, index?: number | EnumType): Decoded { + // If `index` is set, we parse it. + if (index instanceof EnumType) { + return EnumType.createValue(def, index._index, index.raw); + } else if (isNumber(index)) { + return EnumType.createValue(def, index, value); + } + + // Or else, we just look at `value` + return EnumType.decodeViaValue(def, aliasses, value); + } + + private static decodeViaValue (def: TypesDef, aliasses: Aliasses, value?: any): Decoded { + if (value instanceof EnumType) { + return EnumType.createValue(def, value._index, value.raw); + } else if (isU8a(value)) { + return EnumType.createValue(def, value[0], value.subarray(1)); + } else if (isNumber(value)) { + return EnumType.createValue(def, value); + } else if (isString(value)) { + const _str = value.toString(); + + return isHex(_str) + ? EnumType.decodeViaValue(def, aliasses, hexToU8a(_str)) + : EnumType.createViaJSON(def, aliasses, _str); + } else if (isObject(value)) { + const key = Object.keys(value)[0]; + + return EnumType.createViaJSON(def, aliasses, key, value[key]); + } + + // Worst-case scenario, return the first with default + return EnumType.createValue(def, 0); + } + + private static createViaJSON (def: TypesDef, _aliasses: Aliasses, key: string, value?: any) { + // JSON comes in the form of { "": "" }, here we + // additionally force to lower to ensure forward compat + const keys = Object.keys(def).map((k) => k.toLowerCase()); + + // FIXME This whole lookup process is not vey efficient + const keyLower = key.toLowerCase(); + const aliasses = Object.keys(_aliasses).reduce((aliasses, a) => { + aliasses[a.toLowerCase()] = _aliasses[a].toLowerCase(); + + return aliasses; + }, {} as Aliasses); + const aliasKey = aliasses[keyLower] || keyLower; + const index = keys.indexOf(aliasKey); + + assert(index !== -1, `Cannot map input on JSON, unable to find '${key}' in ${keys.join(', ')}`); + + return EnumType.createValue(def, index, value); + } + + private static createValue (def: TypesDef, index: number = 0, value?: any): Decoded { + return { + index, + value: new (Object.values(def)[index])(value) + }; + } + + static with< + S extends ConstructorDef + > (Types: S): EnumConstructor> { + return class extends EnumType { + constructor (value?: any, index?: number) { + super(Types, value, index); + } + }; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return 1 + this.raw.encodedLength; + } + + /** + * @description The index of the metadata value + */ + get index (): number { + return this._index; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.isEmpty; + } + + /** + * @description Checks if the Enum points to a [[Null]] type + */ + get isNone (): boolean { + return this.isNull; + } + + /** + * @description Checks if the Enum points to a [[Null]] type (deprecated, use isNone) + */ + get isNull (): boolean { + return this.raw instanceof Null; + } + + /** + * @description The name of the type this enum value represents + */ + get type (): string { + return Object.keys(this._def)[this._index]; + } + + /** + * @description The value of the enum + */ + get value (): Codec { + return this.raw; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + // cater for the case where we only pass the enum index + if (isNumber(other)) { + return this.toNumber() === other; + } + + // compare the actual wrapper value + return this.value.eq(other); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return { + [this.type]: this.raw.toJSON() + }; + } + + /** + * @description Returns the number representation for the value + */ + toNumber (): number { + return this._index; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.isNull + ? this.type + : JSON.stringify(this.toJSON()); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + const index = this._indexes[this._index]; + + return u8aConcat( + new Uint8Array([index]), + this.raw.toU8a(isBare) + ); + } +} diff --git a/packages/types/src/codec/Int.spec.ts b/packages/types/src/codec/Int.spec.ts new file mode 100644 index 0000000000..3bf15bfa3f --- /dev/null +++ b/packages/types/src/codec/Int.spec.ts @@ -0,0 +1,51 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Int from './Int'; + +describe('Int', () => { + it('provides a toBn interface', () => { + expect( + new Int(-1234).toBn().toNumber() + ).toEqual(-1234); + }); + + it('provides a toNumber interface', () => { + expect( + new Int(-1234).toNumber() + ).toEqual(-1234); + }); + + it('converts to LE from the provided value', () => { + expect( + new Int(-1234).toU8a() + ).toEqual(new Uint8Array([46, 251, 255, 255, 255, 255, 255, 255])); + }); + + it('converts to LE from the provided value (bitLength)', () => { + expect( + new Int(-1234, 32).toU8a() + ).toEqual(new Uint8Array([46, 251, 255, 255])); + }); + + it('converts to hex/string', () => { + const i = new Int('0x12', 16); + + expect(i.toHex()).toEqual('0x0012'); + expect(i.toString()).toEqual('18'); + }); + + it('converts to equivalents', () => { + const a = new Int('-123'); + + expect( + new Int(a).toNumber() + ).toEqual(-123); + }); + + it('converts to JSON depending on flags', () => { + expect(new Int(0x12, 16).toJSON()).toEqual('0x0012'); + expect(new Int(0x12, 16, false).toJSON()).toEqual(0x12); + }); +}); diff --git a/packages/types/src/codec/Int.ts b/packages/types/src/codec/Int.ts new file mode 100644 index 0000000000..25daa16a81 --- /dev/null +++ b/packages/types/src/codec/Int.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import { bnToHex, bnToU8a } from '@plugnet/util'; + +import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt'; + +/** + * @name Int + * @description + * A generic signed integer codec. For Substrate all numbers are LE encoded, + * this handles the encoding and decoding of those numbers. Upon construction + * the bitLength is provided and any additional use keeps the number to this + * length. This extends `BN`, so all methods available on a normal `BN` object + * is available here. + * @noInheritDoc + */ +export default class Int extends AbstractInt { + constructor ( + value: AnyNumber = 0, + bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = true) { + super( + true, + value, + bitLength, + isHexJson + ); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return bnToHex(this, { + bitLength: this._bitLength, + isLe: false, + isNegative: true + }); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return bnToU8a(this, { + bitLength: this._bitLength, + isLe: true, + isNegative: true + }); + } +} diff --git a/packages/types/src/codec/Linkage.ts b/packages/types/src/codec/Linkage.ts new file mode 100644 index 0000000000..82116158dc --- /dev/null +++ b/packages/types/src/codec/Linkage.ts @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Struct, Option, Tuple, Vector } from '.'; +import { Constructor, Codec } from '../types'; + +type TypeWithValues = [Constructor, Array]; + +export class Linkage extends Struct { + constructor (Type: Constructor, value?: any) { + super({ + previous: Option.with(Type), + next: Option.with(Type) + }, value); + } + + static withKey (Type: Constructor): Constructor> { + return class extends Linkage { + constructor (value?: any) { + super(Type, value); + } + }; + } + + get previous (): Option { + return this.get('previous') as Option; + } + + get next (): Option { + return this.get('next') as Option; + } +} + +export class LinkageResult extends Tuple { + constructor ([TypeKey, keys]: TypeWithValues, [TypeValue, values]: TypeWithValues) { + super({ + Keys: Vector.with(TypeKey), + Values: Vector.with(TypeValue) + }, [keys, values]); + } +} diff --git a/packages/types/src/codec/Option.spec.ts b/packages/types/src/codec/Option.spec.ts new file mode 100644 index 0000000000..0f4b8857bb --- /dev/null +++ b/packages/types/src/codec/Option.spec.ts @@ -0,0 +1,88 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Option from './Option'; +import Text from '../primitive/Text'; + +const testDecode = (type: string, input: any, expected: any) => + it(`can decode from ${type}`, () => { + const o = new Option(Text, input); + + expect(o.toString()).toBe(expected); + expect(o.isNone).toBe(!expected.length); + }); + +const testEncode = (to: string, expected: any) => + it(`can encode ${to}`, () => { + const e = new Option(Text, 'foo'); + + expect((e as any)[to]()).toEqual(expected); + }); + +describe('Option', () => { + it('converts undefined/null to empty', () => { + expect(new Option(Text, undefined).isNone).toBe(true); + expect(new Option(Text, null).isNone).toBe(true); + expect(new Option(Text, 'test').isNone).toBe(false); + }); + + it('converts an option to an option', () => { + expect( + new Option(Text, new Option(Text, 'hello')).toString() + ).toEqual('hello'); + }); + + testDecode('string (with)', 'foo', 'foo'); + testDecode('string (without)', undefined, ''); + testDecode('Uint8Array (with)', Uint8Array.from([1, 12, 102, 111, 111]), 'foo'); + testDecode('Uint8Array (without)', Uint8Array.from([0]), ''); + + testEncode('toHex', '0x010c666f6f'); + testEncode('toString', 'foo'); + testEncode('toU8a', Uint8Array.from([1, 12, 102, 111, 111])); + + it('has empty toString() (undefined)', () => { + expect( + new Option(Text).toString() + ).toEqual(''); + }); + + it('has value toString() (provided)', () => { + expect( + new Option(Text, new Uint8Array([1, 4 << 2, 49, 50, 51, 52])).toString() + ).toEqual('1234'); + }); + + it('converts toU8a() with', () => { + expect( + new Option(Text, '1234').toU8a() + ).toEqual(new Uint8Array([1, 4 << 2, 49, 50, 51, 52])); + }); + + it('converts toU8a() without', () => { + expect( + new Option(Text).toU8a() + ).toEqual(new Uint8Array([0])); + }); + + describe('utils', () => { + const testeq = new Option(Text, '1234'); + + it('compares against other option', () => { + expect(testeq.eq(new Option(Text, '1234'))).toBe(true); + }); + + it('compares against raw value', () => { + expect(testeq.eq('1234')).toBe(true); + }); + + it('unwraps to default if empty', () => { + expect(new Option(Text).unwrapOr('6789')).toBe('6789'); + }); + + it('unwraps to value if non-empty', () => { + expect((new Option(Text, '1234').unwrapOr(null) as Text).toString()).toBe('1234'); + }); + }); +}); diff --git a/packages/types/src/codec/Option.ts b/packages/types/src/codec/Option.ts new file mode 100644 index 0000000000..357d3887ca --- /dev/null +++ b/packages/types/src/codec/Option.ts @@ -0,0 +1,160 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isNull, isU8a, isUndefined, u8aToHex } from '@plugnet/util'; + +import Base from './Base'; +import { Codec, Constructor } from '../types'; +import Null from '../primitive/Null'; + +/** + * @name Option + * @description + * An Option is an optional field. Basically the first byte indicates that there is + * is value to follow. If the byte is `1` there is an actual value. So the Option + * implements that - decodes, checks for optionality and wraps the required structure + * with a value if/as required/found. + */ +export default class Option extends Base implements Codec { + constructor (Type: Constructor, value?: any) { + super( + Option.decodeOption(Type, value) + ); + } + + static decodeOption (Type: Constructor, value?: any): Codec { + if (isNull(value) || isUndefined(value) || value instanceof Null) { + return new Null(); + } else if (value instanceof Option) { + return Option.decodeOption(Type, value.value); + } else if (value instanceof Type) { + // don't re-create, use as it (which also caters for derived types) + return value; + } else if (isU8a(value)) { + // the isU8a check happens last in the if-tree - since the wrapped value + // may be an instance of it, so Type and Option checks go in first + return value[0] === 0 + ? new Null() + : new Type(value.subarray(1)); + } + + return new Type(value); + } + + static with (Type: Constructor): Constructor> { + return class extends Option { + constructor (value?: any) { + super(Type, value); + } + }; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + // boolean byte (has value, doesn't have) along with wrapped length + return 1 + this.raw.encodedLength; + } + + /** + * @description Checks if the Option has no value + */ + get isEmpty (): boolean { + return this.isNone; + } + + /** + * @description Checks if the Option has no value + */ + get isNone (): boolean { + return this.raw instanceof Null; + } + + /** + * @description Checks if the Option has a value + */ + get isSome (): boolean { + return !this.isNone; + } + + /** + * @description The actual value for the Option + */ + get value (): Codec { + return this.raw; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + if (other instanceof Option) { + return (this.isSome === other.isSome) && this.value.eq(other.value); + } + + return this.value.eq(other); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.raw.toJSON(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.raw.toString(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + if (isBare) { + return this.raw.toU8a(true); + } + + const u8a = new Uint8Array(this.encodedLength); + + if (this.isSome) { + u8a.set([1]); + u8a.set(this.raw.toU8a(), 1); + } + + return u8a; + } + + /** + * @description Returns the value that the Option represents (if available), throws if null + */ + unwrap (): T { + if (this.isNone) { + throw new Error('Option: unwrapping a None value'); + } + + return this.raw; + } + + /** + * @description Returns the value that the Option represents (if available) or defaultValue if none + * @param defaultValue The value to return if the option isNone + */ + unwrapOr (defaultValue: O): T | O { + return this.isSome + ? this.unwrap() + : defaultValue; + } +} diff --git a/packages/types/src/codec/Set.spec.ts b/packages/types/src/codec/Set.spec.ts new file mode 100644 index 0000000000..fbda18c291 --- /dev/null +++ b/packages/types/src/codec/Set.spec.ts @@ -0,0 +1,73 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Set from './Set'; + +const SET_FIELDS = { + header: 0b00000001, + body: 0b00000010, + receipt: 0b00000100, + messageQueue: 0b00001000, + justification: 0b00010000 +}; +const SET_ROLES = { + none: 0b00000000, + full: 0b00000001, + light: 0b00000010, + authority: 0b00000100 +}; + +describe('Set', () => { + it('constructs via an Array', () => { + const set = new Set(SET_ROLES, ['full', 'authority', 'invalid']); + + expect(set.isEmpty).toEqual(false); + expect(set.toString()).toEqual( + '[full, authority]' + ); + }); + + it('constructs via Uint8Array', () => { + const set = new Set(SET_FIELDS, new Uint8Array([0b00000001 | 0b00000010 | 0b00010000 | 0b10000000])); + + expect(set.encodedLength).toEqual(1); + expect(set.toJSON()).toEqual([ + 'header', 'body', 'justification' + ]); + }); + + it('hash a valid encoding', () => { + const set = new Set(SET_FIELDS, ['header', 'body', 'justification']); + + expect(set.toU8a()).toEqual(new Uint8Array([19])); + }); + + describe('utils', () => { + const set = new Set(SET_ROLES, ['full', 'authority']); + + it('compares against string array', () => { + expect( + set.eq(['authority', 'full']) + ).toBe(true); + }); + + it('compares against number (encoded)', () => { + expect( + set.eq(SET_ROLES.full | SET_ROLES.authority) + ).toBe(true); + }); + + it('compares against other sets', () => { + expect( + set.eq(new Set(SET_ROLES, ['authority', 'full'])) + ).toBe(true); + }); + + it('returns false on other values', () => { + expect( + set.eq('full') + ).toBe(false); + }); + }); +}); diff --git a/packages/types/src/codec/Set.ts b/packages/types/src/codec/Set.ts new file mode 100644 index 0000000000..96fa3d3534 --- /dev/null +++ b/packages/types/src/codec/Set.ts @@ -0,0 +1,142 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a, isNumber, isUndefined, u8aToHex } from '@plugnet/util'; + +import Base from './Base'; +import { Codec } from '../types'; +import { compareArray } from './utils'; + +type SetValues = { + [index: string]: number +}; + +/** + * @name Set + * @description + * An Set is an array of string values, represented an an encoded type by + * a bitwise representation of the values. + */ +// FIXME This is a prime candidate to extend the JavaScript built-in Set +export default class Set extends Base> implements Codec { + private _setValues: SetValues; + + constructor (setValues: SetValues, value?: Array | Uint8Array | number) { + super( + Set.decodeSet(setValues, value) + ); + + this._setValues = setValues; + } + + static decodeSet (setValues: SetValues, value: Array | Uint8Array | number = 0): Array { + if (isU8a(value)) { + return Set.decodeSet(setValues, value[0]); + } else if (Array.isArray(value)) { + return value.reduce((result, value) => { + if (isUndefined(setValues[value])) { + console.error(`Ignoring invalid '${value}' passed to Set`); + } else { + result.push(value); + } + + return result; + }, [] as Array); + } + + const result = Object.keys(setValues).reduce((result, key) => { + if ((value & setValues[key]) === setValues[key]) { + result.push(key); + } + + return result; + }, [] as Array); + const computed = Set.encodeSet(setValues, result); + + if (value !== computed) { + console.error(`Mismatch decoding '${value}', computed as '${computed}' with ${result}`); + } + + return result; + } + + static encodeSet (setValues: SetValues, value: Array): number { + return value.reduce((result, value) => { + return result | (setValues[value] || 0); + }, 0); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return 1; + } + + /** + * @description true is the Set contains no values + */ + get isEmpty (): boolean { + return this.values.length === 0; + } + + /** + * @description The actual set values as a Array + */ + get values (): Array { + return this.raw; + } + + /** + * @description The encoded value for the set members + */ + get valueEncoded (): number { + return Set.encodeSet(this._setValues, this.raw); + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + if (Array.isArray(other)) { + // we don't actually care about the order, sort the values + return compareArray(this.values.sort(), other.sort()); + } else if (other instanceof Set) { + return this.eq(other.values); + } else if (isNumber(other)) { + return this.valueEncoded === other; + } + + return false; + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.values; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return `[${this.values.join(', ')}]`; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return new Uint8Array([this.valueEncoded]); + } +} diff --git a/packages/types/src/codec/Struct.spec.ts b/packages/types/src/codec/Struct.spec.ts new file mode 100644 index 0000000000..7230e452a8 --- /dev/null +++ b/packages/types/src/codec/Struct.spec.ts @@ -0,0 +1,186 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; +import BlockNumber from '../type/BlockNumber'; +import { CodecTo } from '../types'; +import Option from './Option'; +import Struct from './Struct'; +import Vector from './Vector'; + +describe('Struct', () => { + describe('decoding', () => { + const testDecode = (type: string, input: any) => + it(`can decode from ${type}`, () => { + const s = new Struct({ + foo: Text, + bar: U32 + }, input); + expect([...s.keys()]).toEqual(['foo', 'bar']); + expect( + [...s.values()].map((v) => + v.toString() + ) + ).toEqual(['bazzing', '69']); + }); + + testDecode('array', ['bazzing', 69]); + testDecode('hex', '0x1c62617a7a696e6745000000'); + testDecode('object', { foo: 'bazzing', bar: 69 }); + testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); + }); + + describe('encoding', () => { + const testEncode = (to: CodecTo, expected: any) => + it(`can encode ${to}`, () => { + const s = new Struct({ + foo: Text, + bar: U32 + }, { foo: 'bazzing', bar: 69 }); + expect(s[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x1c62617a7a696e6745000000'); + testEncode('toJSON', { foo: 'bazzing', bar: 69 }); + testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); + testEncode('toString', '{"foo":"bazzing","bar":69}'); + }); + + it('decodes null', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )(null).toString() + ).toEqual('{}'); + }); + + it('decodes a more complicated type', () => { + const s = new Struct({ + foo: Vector.with(Struct.with({ + bar: Text + })) + }, { foo: [{ bar: 1 }, { bar: 2 }] }); + expect(s.toString()).toBe('{"foo":[{"bar":"1"},{"bar":"2"}]}'); + }); + + it('decodes from a Map input', () => { + const input = new Struct({ + a: U32, + txt: Text + }, { a: 42, txt: 'fubar' }); + const s = new Struct({ + txt: Text, + foo: U32, + bar: U32 + }, input); + expect(s.toString()).toEqual('{"txt":"fubar","foo":0,"bar":0}'); + }); + + it('throws when it cannot decode', () => { + expect( + () => new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )('ABC') + ).toThrowError(/Struct: cannot decode type/); + }); + + it('provides a clean toString()', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ txt: 'foo', u32: 0x123456 }).toString() + ).toEqual('{"txt":"foo","u32":1193046}'); + }); + + it('exposes the properties on the object', () => { + const struct = new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ txt: 'foo', u32: 0x123456 }); + + expect((struct as any).txt.toString()).toEqual('foo'); + expect((struct as any).u32.toNumber()).toEqual(0x123456); + }); + + it('correctly encodes length', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ foo: 'bazzing', bar: 69 }).encodedLength + ).toEqual(5); + }); + + it('exposes the types', () => { + expect( + new Struct({ + foo: Text, + bar: Text, + baz: U32 + }, { + foo: 'foo', + bar: 'bar', + baz: 3 + }).Type + ).toEqual({ + foo: 'Text', + bar: 'Text', + baz: 'U32' + }); + }); + + it('gets the value at a particular index', () => { + expect( + new ( + Struct.with({ + txt: Text, + u32: U32 + }) + )({ txt: 'foo', u32: 1234 }) + .getAtIndex(1) + .toString() + ).toEqual('1234'); + }); + + describe('utils', () => { + it('compares against other objects', () => { + const test = { + foo: 'foo', + bar: 'bar', + baz: 3 + }; + + expect( + new Struct({ + foo: Text, + bar: Text, + baz: U32 + }, test).eq(test) + ).toBe(true); + }); + }); + + it('allows toString with large numbers', () => { + // replicate https://github.com/polkadot-js/api/issues/640 + expect( + new Struct({ + blockNumber: Option.with(BlockNumber) + }, { blockNumber: '0x1234567890abcdef' }).toString() + ).toEqual('{"blockNumber":"0x1234567890abcdef"}'); + }); +}); diff --git a/packages/types/src/codec/Struct.ts b/packages/types/src/codec/Struct.ts new file mode 100644 index 0000000000..d3d8b5fbe2 --- /dev/null +++ b/packages/types/src/codec/Struct.ts @@ -0,0 +1,241 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a, isHex, isObject, isU8a, u8aConcat, u8aToHex } from '@plugnet/util'; + +import { Codec, Constructor, ConstructorDef } from '../types'; +import { compareMap, decodeU8a } from './utils'; + +/** + * @name Struct + * @description + * A Struct defines an Object with key/values - where the values are Codec values. It removes + * a lot of repetition from the actual coding, define a structure type, pass it the key/Codec + * values in the constructor and it manages the decoding. It is important that the constructor + * values matches 100% to the order in th Rust code, i.e. don't go crazy and make it alphabetical, + * it needs to decoded in the specific defined order. + * @noInheritDoc + */ +export default class Struct< + // The actual Class structure, i.e. key -> Class + S extends ConstructorDef = ConstructorDef, + // internal type, instance of classes mapped by key + T extends { [K in keyof S]: Codec } = { [K in keyof S]: Codec }, + // input values, mapped by key can be anything (construction) + V extends { [K in keyof S]: any } = { [K in keyof S]: any }, + // type names, mapped by key, name of Class in S + E extends { [K in keyof S]: string } = { [K in keyof S]: string } + > extends Map implements Codec { + protected _jsonMap: Map; + protected _Types: E; + + constructor (Types: S, value: V | Map | Array = {} as V, jsonMap: Map = new Map()) { + const decoded = Struct.decodeStruct(Types, value, jsonMap); + + super( + Object.entries(decoded) + ); + + this._jsonMap = jsonMap; + this._Types = (Object + .keys(Types) as Array) + .reduce((result: E, key) => { + // TS2322: Type 'string' is not assignable to type 'E[keyof S]'. + (result as any)[key] = Types[key].name; + + return result; + }, {} as E); + } + + /** + * Decode input to pass into constructor. + * + * @param Types - Types definition. + * @param value - Value to decode, one of: + * - null + * - undefined + * - hex + * - Uint8Array + * - object with `{ key1: value1, key2: value2 }`, assuming `key1` and `key2` + * are also keys in `Types` + * - array with `[value1, value2]` assuming the array has the same length as + * `Object.keys(Types)` + * @param jsonMap + */ + private static decodeStruct< + S extends ConstructorDef, + _, + T extends { [K in keyof S]: Codec } + > (Types: S, value: any, jsonMap: Map): T { + // l.debug(() => ['Struct.decode', { Types, value }]); + + if (isHex(value)) { + return Struct.decodeStruct(Types, hexToU8a(value as string), jsonMap); + } else if (isU8a(value)) { + const values = decodeU8a(value, Object.values(Types)); + + // Transform array of values to {key: value} mapping + return Object.keys(Types).reduce((raw: T, key: keyof S, index) => { + // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. + (raw as any)[key] = values[index]; + return raw; + }, {} as T); + } else if (!value) { + return {} as T; + } + + // We assume from here that value is a JS object (Array, Map, Object) + return Struct.decodeStructFromObject(Types, value, jsonMap); + } + + private static decodeStructFromObject< + S extends ConstructorDef, + _, + T extends { [K in keyof S]: Codec } + > (Types: S, value: any, jsonMap: Map): T { + return Object.keys(Types).reduce((raw: T, key: keyof S, index) => { + // The key in the JSON can be snake_case (or other cases), but in our + // Types, result or any other maps, it's camelCase + const jsonKey = (jsonMap.get(key as any) && !value[key]) ? jsonMap.get(key as any) : key; + + if (Array.isArray(value)) { + raw[key] = value[index] instanceof Types[key] + ? value[index] + : new Types[key](value[index]); + } else if (value instanceof Map) { + const mapped = value.get(jsonKey); + + // TS2322: Type 'Codec' is not assignable to type 'T[keyof S]'. + (raw as any)[key] = mapped instanceof Types[key] + ? mapped + : new Types[key](mapped); + } else if (isObject(value)) { + raw[key] = value[jsonKey as string] instanceof Types[key] + ? value[jsonKey as string] + : new Types[key](value[jsonKey as string]); + } else { + throw new Error(`Struct: cannot decode type ${Types[key].name} with value ${JSON.stringify(value)}`); + } + + return raw; + }, {} as T); + } + + static with< + S extends ConstructorDef + > (Types: S): Constructor> { + return class extends Struct { + constructor (value?: any, jsonMap?: Map) { + super(Types, value, jsonMap); + + (Object.keys(Types) as Array).forEach((key) => { + Object.defineProperty(this, key, { + enumerable: true, + get: () => this.get(key) + }); + }); + } + }; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + const items = this.toArray(); + + for (let i = 0; i < items.length; i++) { + if (!items[i].isEmpty) { + return false; + } + } + + return true; + } + + /** + * @description Returns the Type description to sthe structure + */ + get Type (): E { + return this._Types; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.toArray().reduce((length, entry) => { + return length += entry.encodedLength; + }, 0); + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return compareMap(this, other); + } + + /** + * @description Returns a specific names entry in the structure + * @param name The name of the entry to retrieve + */ + get (name: keyof S): Codec | undefined { + return super.get(name); + } + + /** + * @description Returns the values of a member at a specific index (Rather use get(name) for performance) + */ + getAtIndex (index: number): Codec { + return this.toArray()[index]; + } + + /** + * @description Converts the Object to an standard JavaScript Array + */ + toArray (): Array { + return [...this.values()]; + } + + /** + * @description Returns a hex string representation of the value + */ + toHex () { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return [...this.keys()].reduce((json, key) => { + const jsonKey = this._jsonMap.get(key) || key; + + const value = this.get(key); + json[jsonKey] = value && value.toJSON(); + + return json; + }, {} as any); + } + + /** + * @description Returns the string representation of the value + */ + toString () { + return JSON.stringify(this.toJSON()); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return u8aConcat( + ...this.toArray().map((entry) => + entry.toU8a(isBare) + ) + ); + } +} diff --git a/packages/types/src/codec/Tuple.spec.ts b/packages/types/src/codec/Tuple.spec.ts new file mode 100644 index 0000000000..5a0c793a84 --- /dev/null +++ b/packages/types/src/codec/Tuple.spec.ts @@ -0,0 +1,97 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsics from '@plugnet/extrinsics/static'; + +import { CodecTo } from '../types'; +import Method from '../primitive/Method'; +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; +import BlockNumber from '../type/BlockNumber'; +import Proposal from '../type/Proposal'; +import VoteThreshold from '../type/VoteThreshold'; +import Tuple from './Tuple'; + +describe('Tuple', () => { + let tuple: Tuple; + + beforeEach(() => { + tuple = new Tuple( + [Text, U32], + ['bazzing', 69] + ); + }); + + describe('decoding', () => { + const testDecode = (type: string, input: any) => + it(`can decode from ${type}`, () => { + const t = new Tuple([ + Text, + U32 + ], input); + expect( + t.toJSON() + ).toEqual(['bazzing', 69]); + }); + + testDecode('array', ['bazzing', 69]); + testDecode('hex', '0x1c62617a7a696e6745000000'); + testDecode('Uint8Array', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); + }); + + describe('encoding', () => { + const testEncode = (to: CodecTo | 'toArray', expected: any) => + it(`can encode ${to}`, () => { + expect(tuple[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x1c62617a7a696e6745000000'); + testEncode('toJSON', ['bazzing', 69]); + testEncode('toU8a', Uint8Array.from([28, 98, 97, 122, 122, 105, 110, 103, 69, 0, 0, 0])); + testEncode('toString', '["bazzing",69]'); + }); + + it.skip('creates properly via actual hex string', () => { + Method.injectMethods(extrinsics); + + const test = new (Tuple.with([ + BlockNumber, Proposal, VoteThreshold + ] + ))('0x62190000000000000003507b0a092230783432223a202230783433220a7d0a01'); + + expect((test[0] as BlockNumber).toNumber()).toEqual(6498); + expect((test[1] as Method).callIndex).toEqual(new Uint8Array([0, 3])); + expect((test[2] as VoteThreshold).toNumber()).toEqual(1); + }); + + it('exposes the Types', () => { + expect(tuple.Types).toEqual(['Text', 'U32']); + }); + + it('exposes the Types (object creation)', () => { + const test = new Tuple({ + BlockNumber, VoteThreshold + }, []); + + expect(test.Types).toEqual(['BlockNumber', 'VoteThreshold']); + }); + + it('exposes filter', () => { + expect(tuple.filter((v) => v.toJSON() === 69)).toEqual([new U32(69)]); + }); + + it('exposes map', () => { + expect(tuple.map(v => v.toString())).toEqual(['bazzing', '69']); + }); + + describe('utils', () => { + it('compares against inputs', () => { + expect(tuple.eq(['bazzing', 69])).toBe(true); + }); + + it('compares against inputs (mismatch)', () => { + expect(tuple.eq(['bazzing', 72])).toBe(false); + }); + }); +}); diff --git a/packages/types/src/codec/Tuple.ts b/packages/types/src/codec/Tuple.ts new file mode 100644 index 0000000000..decc544f04 --- /dev/null +++ b/packages/types/src/codec/Tuple.ts @@ -0,0 +1,152 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a, u8aConcat, u8aToHex, isHex, hexToU8a } from '@plugnet/util'; + +import { AnyNumber, AnyU8a, AnyString, Codec, Constructor } from '../types'; +import { compareArray, decodeU8a } from './utils'; + +type TupleConstructors = Array | { + [index: string]: Constructor +}; + +/** + * @name Tuple + * @description + * A Tuple defines an anonymous fixed-length array, where each element has its + * own type. It extends the base JS `Array` object. + * @noInheritDoc + */ +export default class Tuple extends Array implements Codec { + private _Types: TupleConstructors; + + constructor (Types: TupleConstructors, value: any) { + super( + ...Tuple.decodeTuple(Types, value) + ); + + this._Types = Types; + } + + private static decodeTuple (_Types: TupleConstructors, value: AnyU8a | string | Array): Array { + if (isU8a(value)) { + return decodeU8a(value, _Types); + } else if (isHex(value)) { + return Tuple.decodeTuple(_Types, hexToU8a(value)); + } + + const Types: Array = Array.isArray(_Types) + ? _Types + : Object.values(_Types); + + return Types.map((Type, index) => { + return new Type(value && value[index]); + }); + } + + static with (Types: TupleConstructors): Constructor { + return class extends Tuple { + constructor (value?: any) { + super(Types, value); + } + }; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.reduce((length, entry) => { + return length += entry.encodedLength; + }, 0); + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.length === 0; + } + + /** + * @description The types definition of the tuple + */ + get Types (): Array { + return Array.isArray(this._Types) + ? this._Types.map(({ name }) => name) + : Object.keys(this._Types); + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return compareArray(this, other); + } + + /** + * @description Converts the Object to an standard JavaScript Array + */ + toArray (): Array { + return Array.from(this); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex () { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.map((entry) => + entry.toJSON() + ); + } + + /** + * @description Returns the string representation of the value + */ + toString () { + // Overwrite the default toString representation of Array. + return JSON.stringify(this.toJSON()); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return u8aConcat( + ...this.map((entry) => + entry.toU8a(isBare) + ) + ); + } + + // Below are methods that we override. When we do a `new Tuple(...).map()`, + // we want it to throw an error. We only override the methods that return a + // new instance. + + /** + * @description Filters the array with the callback + * @param callbackfn The filter function + * @param thisArg The `this` object to apply the result to + */ + filter (callbackfn: (value: Codec, index: number, array: Array) => any, thisArg?: any): Array { + return this.toArray().filter(callbackfn, thisArg); + } + + /** + * @description Maps the array with the callback + * @param callbackfn The mapping function + * @param thisArg The `this` onject to apply the result to + */ + map (callbackfn: (value: Codec, index: number, array: Array) => U, thisArg?: any): Array { + return this.toArray().map(callbackfn, thisArg); + } +} diff --git a/packages/types/src/codec/TypeRegistry.spec.ts b/packages/types/src/codec/TypeRegistry.spec.ts new file mode 100644 index 0000000000..e264625a77 --- /dev/null +++ b/packages/types/src/codec/TypeRegistry.spec.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { TypeRegistry } from './typeRegistry'; +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; + +describe('TypeRegistry', () => { + let registry: TypeRegistry; + + beforeEach(() => { + registry = new TypeRegistry(); + }); + + it('handles non exist type', () => { + expect(registry.get('non-exist')).toBeUndefined(); + }); + + it('can register single type', () => { + registry.register(Text); + expect(registry.get('Text')).toBe(Text); + }); + + it('can register type with a different name', () => { + registry.register('TextRenamed', Text); + expect(registry.get('TextRenamed')).toBe(Text); + }); + + describe('object registration', () => { + it('can register multiple types', () => { + registry.register({ + Text, + 'U32Renamed': U32 + }); + expect(registry.get('Text')).toBe(Text); + expect(registry.get('U32Renamed')).toBe(U32); + }); + + it('can create types from string', () => { + registry.register({ + 'U32Renamed': 'u32' + }); + expect(registry.get('U32Renamed')).toBe(U32); + }); + + it('can create structs via definition', () => { + registry.register({ + 'SomeStruct': { + 'foo': 'u32', + 'bar': 'Text' + } + }); + + const SomeStruct: any = registry.get('SomeStruct'); + const struct: any = new SomeStruct({ + foo: 42, + bar: 'testing' + }); + + expect(SomeStruct.name).toBe('Struct'); + expect(struct.get('foo').toNumber()).toEqual(42); + expect(struct.get('bar').toString()).toEqual('testing'); + }); + }); +}); diff --git a/packages/types/src/codec/U8a.spec.ts b/packages/types/src/codec/U8a.spec.ts new file mode 100644 index 0000000000..4eec497ab6 --- /dev/null +++ b/packages/types/src/codec/U8a.spec.ts @@ -0,0 +1,72 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { CodecTo } from '../types'; +import U8a from './U8a'; + +const testDecode = (type: string, input: any, expected: string) => + it(`can decode from ${type}`, () => { + const e = new U8a(input); + expect(e.toString()).toBe(expected); + }); + +const testEncode = (to: CodecTo, expected: any) => + it(`can encode ${to}`, () => { + const e = new U8a([1, 2, 3, 4, 5]); + expect(e[to]()).toEqual(expected); + }); + +describe('U8a', () => { + let u8a: U8a; + + beforeEach(() => { + u8a = new U8a([1, 2, 3, 4, 5]); + }); + + testDecode('Array', [1, 2, 3, 4, 5], '0x0102030405'); + testDecode('hex', '0x0102030405', '0x0102030405'); + testDecode('U8a', new Uint8Array([1, 2, 3, 4, 5]), '0x0102030405'); + testDecode('Uint8Array', Uint8Array.from([1, 2, 3, 4, 5]), '0x0102030405'); + + testEncode('toJSON', '0x0102030405'); + testEncode('toHex', '0x0102030405'); + testEncode('toString', '0x0102030405'); + testEncode('toU8a', Uint8Array.from([1, 2, 3, 4, 5])); + + it('contains the length of the elements', () => { + expect(u8a.length).toEqual(5); + }); + + it('correctly encodes length', () => { + expect(u8a.encodedLength).toEqual(5); + }); + + it('allows wrapping of a pre-existing instance', () => { + expect( + new U8a(u8a).length + ).toEqual(5); + }); + + it('implements subarray correctly', () => { + expect(u8a.subarray(1, 3)).toEqual(Uint8Array.from([2, 3])); + }); + + describe('utils', () => { + it('compares against other U8a', () => { + expect(u8a.eq(new Uint8Array([1, 2, 3, 4, 5]))).toBe(true); + }); + + it('compares against other U8a (non-length)', () => { + expect(u8a.eq(new Uint8Array([1, 2, 3, 4]))).toBe(false); + }); + + it('compares against other U8a (mismatch)', () => { + expect(u8a.eq(new Uint8Array([1, 2, 3, 4, 6]))).toBe(false); + }); + + it('compares against hex inputs', () => { + expect(u8a.eq('0x0102030405')).toBe(true); + }); + }); +}); diff --git a/packages/types/src/codec/U8a.ts b/packages/types/src/codec/U8a.ts new file mode 100644 index 0000000000..c687baa72c --- /dev/null +++ b/packages/types/src/codec/U8a.ts @@ -0,0 +1,105 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a, isUndefined, u8aToHex, u8aToU8a } from '@plugnet/util'; + +import { AnyU8a, Codec } from '../types'; + +/** + * @name U8a + * @description + * A basic wrapper around Uint8Array, with no frills and no fuss. It does differ + * from other implementations wher it will consume the full Uint8Array as passed to + * it. As such it is meant to be subclassed where the wrapper takes care of the + * actual lengths instead of used directly. + * @noInheritDoc + */ +export default class U8a extends Uint8Array implements Codec { + constructor (value?: AnyU8a) { + super( + U8a.decodeU8a(value) + ); + } + + private static decodeU8a (value?: any): Uint8Array { + if (isU8a(value)) { + return value; + } + + return u8aToU8a(value); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.length; + } + + /** + * @description Returns true if the type wraps an empty/default all-0 value + */ + get isEmpty (): boolean { + return !this.length || isUndefined(this.find((value) => !!value)); + } + + /** + * @description The length of the value + */ + get length (): number { + // only included here since we ignore inherited docs + return super.length; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + if (other instanceof Uint8Array) { + return (this.length === other.length) && isUndefined( + this.find((value, index) => value !== other[index]) + ); + } + + return this.eq(U8a.decodeU8a(other)); + } + + /** + * @description Create a new subarray from the actual buffer. This is needed for compat reasons since a new Uint8Array gets returned here + * @param begin The position to start at + * @param end The position to end at + */ + subarray (begin: number, end?: number): Uint8Array { + return Uint8Array.from(this).subarray(begin, end); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toHex(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.toHex(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return Uint8Array.from(this); + } +} diff --git a/packages/types/src/codec/U8aFixed.spec.ts b/packages/types/src/codec/U8aFixed.spec.ts new file mode 100644 index 0000000000..03ce28c09d --- /dev/null +++ b/packages/types/src/codec/U8aFixed.spec.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U8aFixed from './U8aFixed'; + +describe('U8aFixed', () => { + let u8a: U8aFixed; + + beforeEach(() => { + u8a = new U8aFixed([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 32); + }); + + it('limits the length', () => { + expect(u8a.length).toEqual(4); + }); + + it('exposes the correct bitLength', () => { + expect(u8a.bitLength()).toEqual(32); + }); + + it('allows wrapping of a pre-existing instance', () => { + expect( + u8a.toU8a() + ).toEqual(new Uint8Array([1, 2, 3, 4])); + }); + + it('allows empty values', () => { + expect( + new U8aFixed().toHex() + ).toEqual('0x0000000000000000000000000000000000000000000000000000000000000000'); + }); +}); diff --git a/packages/types/src/codec/U8aFixed.ts b/packages/types/src/codec/U8aFixed.ts new file mode 100644 index 0000000000..0df9dec7d4 --- /dev/null +++ b/packages/types/src/codec/U8aFixed.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isString, isU8a, u8aToU8a } from '@plugnet/util'; + +import { AnyU8a } from '../types'; + +import U8a from './U8a'; + +type BitLength = 8 | 16 | 32 | 64 | 128 | 160 | 256 | 512; + +/** + * @name U8aFixed + * @description + * A U8a that manages a a sequence of bytes up to the specified bitLength. Not meant + * to be used directly, rather is should be subclassed with the specific lengths. + */ +export default class U8aFixed extends U8a { + constructor (value: AnyU8a = new Uint8Array(), bitLength: BitLength = 256) { + super( + U8aFixed.decodeU8aFixed(value, bitLength) + ); + } + + private static decodeU8aFixed (value: AnyU8a, bitLength: BitLength = 256): AnyU8a { + if (isU8a(value)) { + // ensure that we have an actual u8a with the full length as specified by + // the bitLength input (padded with zeros as required) + const byteLength = bitLength / 8; + const sub = value.subarray(0, byteLength); + + if (sub.length === byteLength) { + return sub; + } + + const u8a = new Uint8Array(byteLength); + + u8a.set(sub, 0); + + return u8a; + } else if (Array.isArray(value) || isString(value)) { + return U8aFixed.decodeU8aFixed(u8aToU8a(value), bitLength); + } + + return value; + } + + /** + * @description Returns the number of bits in the value + */ + bitLength () { + return this.length * 8; + } +} diff --git a/packages/types/src/codec/UInt.spec.ts b/packages/types/src/codec/UInt.spec.ts new file mode 100644 index 0000000000..cc167d693b --- /dev/null +++ b/packages/types/src/codec/UInt.spec.ts @@ -0,0 +1,70 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import UInt from './UInt'; + +describe('UInt', () => { + it('provides a toBn interface', () => { + expect( + new UInt(987).toBn().toNumber() + ).toEqual(987); + }); + + it('provides a toNumber interface', () => { + expect( + new UInt(4567).toNumber() + ).toEqual(4567); + }); + + it('converts to LE from the provided value', () => { + expect( + new UInt(1234567).toU8a() + ).toEqual(new Uint8Array([135, 214, 18, 0, 0, 0, 0, 0])); + }); + + it('converts to LE from the provided value (bitLength)', () => { + expect( + new UInt(1234567, 32).toU8a() + ).toEqual(new Uint8Array([135, 214, 18, 0])); + }); + + it('converts to hex/string', () => { + const u = new UInt('0x12', 16); + + expect(u.toHex()).toEqual('0x0012'); + expect(u.toString()).toEqual('18'); + }); + + it('converts to equivalents', () => { + const a = new UInt('123'); + + expect( + new UInt(a).toNumber() + ).toEqual(123); + }); + + it('converts to JSON representation based on flags/size', () => { + expect(new UInt('0x12345678', 64, true).toJSON()).toEqual('0x0000000012345678'); + expect(new UInt('0x1234567890', 64, false).toJSON()).toEqual(0x1234567890); + expect(new UInt('0x1234567890abcdef', 64, false).toJSON()).toEqual('0x1234567890abcdef'); + }); + + describe('eq', () => { + const test = new UInt(12345); + + it('compares against other BN values', () => { + expect(test.eq(new BN(12345))).toBe(true); + }); + + it('compares against other number values', () => { + expect(test.eq(12345)).toBe(true); + }); + + it('compares against hex values', () => { + expect(test.eq('0x3039')).toBe(true); + }); + }); +}); diff --git a/packages/types/src/codec/UInt.ts b/packages/types/src/codec/UInt.ts new file mode 100644 index 0000000000..f803e7b4f4 --- /dev/null +++ b/packages/types/src/codec/UInt.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import { bnToHex, bnToU8a } from '@plugnet/util'; + +import AbstractInt, { DEFAULT_UINT_BITS, UIntBitLength } from './AbstractInt'; + +/** + * @name UInt + * @description + * A generic unsigned integer codec. For Substrate all numbers are LE encoded, + * this handles the encoding and decoding of those numbers. Upon construction + * the bitLength is provided and any additional use keeps the number to this + * length. This extends `BN`, so all methods available on a normal `BN` object + * is available here. + * @noInheritDoc + */ +export default class UInt extends AbstractInt { + constructor ( + value: AnyNumber = 0, + bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = false) { + super( + false, + value, + bitLength, + isHexJson + ); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return bnToHex(this, { + bitLength: this._bitLength, + isLe: false, + isNegative: false + }); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return bnToU8a(this, { + bitLength: this._bitLength, + isLe: true, + isNegative: false + }); + } +} diff --git a/packages/types/src/codec/Vector.spec.ts b/packages/types/src/codec/Vector.spec.ts new file mode 100644 index 0000000000..d8e3f6688a --- /dev/null +++ b/packages/types/src/codec/Vector.spec.ts @@ -0,0 +1,140 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsics from '@plugnet/extrinsics/static'; + +import Method from '../primitive/Method'; +import Text from '../primitive/Text'; +import PropIndex from '../type/PropIndex'; +import Proposal from '../type/Proposal'; +import AccountId from '../type/AccountId'; +import { Codec, CodecTo } from '../types'; +import createType from './createType'; +import Vector from './Vector'; +import Tuple from './Tuple'; + +describe('Vector', () => { + let vector: Vector; + + beforeEach(() => { + vector = new Vector(Text, ['1', '23', '345', '4567', new Text('56789')]); + + Method.injectMethods(extrinsics); + }); + + it('wraps a sequence of values', () => { + expect(vector.length).toEqual(5); // eslint-disable-line + }); + + it('has a sane representation for toString', () => { + expect(vector.toString()).toEqual('[1, 23, 345, 4567, 56789]'); + }); + + it('encodes with length prefix', () => { + expect(vector.toU8a()).toEqual(new Uint8Array([ + 5 << 2, + 1 << 2, 49, + 2 << 2, 50, 51, + 3 << 2, 51, 52, 53, + 4 << 2, 52, 53, 54, 55, + 5 << 2, 53, 54, 55, 56, 57 + ])); + }); + + it('allows contruction via JSON', () => { + expect( + new Vector(Text, ['6', '7']).toJSON() + ).toEqual(['6', '7']); + }); + + it('exposes the type', () => { + expect(vector.Type).toEqual('Text'); + }); + + it.skip('decodes a complex type via construction', () => { + const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([ + 4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79 + ])); + const first = (test as Vector)[0] as Tuple; + + expect((first[0] as PropIndex).toNumber()).toEqual(10); + expect((first[1] as Proposal).callIndex).toEqual(new Uint8Array([0, 3])); + expect((first[2] as AccountId).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ'); + }); + + describe('vector-like functions', () => { + it('allows retrieval of a specific item', () => { + expect( + vector[2].toString() + ).toEqual('345'); + }); + + it('exposes a working forEach', () => { + const result: { [index: number]: string } = {}; + + vector.forEach((e, i) => { + result[i] = e.toString(); + }); + + expect(result).toEqual({ + 0: '1', + 1: '23', + 2: '345', + 3: '4567', + 4: '56789' + }); + }); + + it('exposes a working filter', () => { + expect( + vector.filter((e, i) => i >= 3).toString() + ).toEqual('4567,56789'); + }); + + it('exposes a working map', () => { + expect( + vector.map((e) => e.toString().substr(0, 1)) + ).toEqual(['1', '2', '3', '4', '5']); + }); + + it('exposes a working reduce', () => { + expect( + vector.reduce((r, e) => `${r}${e}`, '') + ).toEqual('123345456756789'); + }); + }); + + describe('encode', () => { + const testEncode = (to: CodecTo, expected: any) => + it(`can encode ${to}`, () => { + expect(vector[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x1404310832330c3334351034353637143536373839'); + testEncode('toJSON', ['1', '23', '345', '4567', '56789']); + testEncode('toString', '[1, 23, 345, 4567, 56789]'); + testEncode('toU8a', Uint8Array.from([20, 4, 49, 8, 50, 51, 12, 51, 52, 53, 16, 52, 53, 54, 55, 20, 53, 54, 55, 56, 57])); + }); + + describe('utils', () => { + const vec = new Vector(Text, ['123', '456']); + + it('compares against codec types', () => { + expect(vec.eq([new Text('123'), new Text('456')])).toBe(true); + }); + + it('compares against codec + primitive types', () => { + expect(vec.eq(['123', new Text('456')])).toBe(true); + }); + + it('finds the index of an value', () => { + const myId = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; + const vec = new Vector(AccountId, [ + '5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw', '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty', '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' + ]); + + expect(vec.indexOf(myId)).toEqual(2); + }); + }); +}); diff --git a/packages/types/src/codec/Vector.ts b/packages/types/src/codec/Vector.ts new file mode 100644 index 0000000000..f8820b6983 --- /dev/null +++ b/packages/types/src/codec/Vector.ts @@ -0,0 +1,185 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aConcat, u8aToU8a, u8aToHex } from '@plugnet/util'; + +import Compact from './Compact'; +import { Codec, Constructor } from '../types'; +import { compareArray, decodeU8a } from './utils'; + +/** + * @name Vector + * @description + * This manages codec arrays. Internally it keeps track of the length (as decoded) and allows + * construction with the passed `Type` in the constructor. It is an extension to Array, providing + * specific encoding/decoding on top of the base type. + * @noInheritDoc + */ +export default class Vector< + T extends Codec + > extends Array implements Codec { + private _Type: Constructor; + + constructor (Type: Constructor, value: Vector | Uint8Array | string | Array = [] as Array) { + super( + ...Vector.decodeVector(Type, value) + ); + + this._Type = Type; + } + + static decodeVector (Type: Constructor, value: Vector | Uint8Array | string | Array): Array { + if (Array.isArray(value)) { + return value.map((entry) => + entry instanceof Type + ? entry + : new Type(entry) + ); + } + + const u8a = u8aToU8a(value); + const [offset, _length] = Compact.decodeU8a(u8a); + const length = _length.toNumber(); + + return decodeU8a(u8a.subarray(offset), new Array(length).fill(Type)) as T[]; + } + + static with (Type: Constructor): Constructor> { + return class extends Vector { + constructor (value?: Array) { + super(Type, value); + } + }; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.length === 0; + } + + /** + * @description The type for the items + */ + get Type (): string { + return this._Type.name; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.reduce((total, raw) => { + return total + raw.encodedLength; + }, Compact.encodeU8a(this.length).length); + } + + /** + * @description The length of the value + */ + get length (): number { + // only included here since we ignore inherited docs + return super.length; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return compareArray(this, other); + } + + /** + * @description Finds the index of the value in the array + */ + indexOf (_other?: any): number { + // convert type first, this removes overhead from the eq + const other = _other instanceof this._Type + ? _other + : new this._Type(_other); + + for (let i = 0; i < this.length; i++) { + if (other.eq(this[i])) { + return i; + } + } + + return -1; + } + + /** + * @description Converts the Object to an standard JavaScript Array + */ + toArray (): Array { + return Array.from(this); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.map((entry) => + entry.toJSON() + ); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + // Overwrite the default toString representation of Array. + const data = this.map((entry) => + entry.toString() + ); + + return `[${data.join(', ')}]`; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + const encoded = this.map((entry) => + entry.toU8a(isBare) + ); + + return isBare + ? u8aConcat(...encoded) + : u8aConcat( + Compact.encodeU8a(this.length), + ...encoded + ); + } + + // Below are methods that we override. When we do a `new Vector(...).map()`, + // we want it to return an Array. We only override the methods that return a + // new instance. + + /** + * @description Filters the array with the callback + * @param callbackfn The filter function + * @param thisArg The `this` object to apply the result to + */ + filter (callbackfn: (value: T, index: number, array: Array) => any, thisArg?: any): Array { + return this.toArray().filter(callbackfn, thisArg); + } + + /** + * @description Maps the array with the callback + * @param callbackfn The mapping function + * @param thisArg The `this` onject to apply the result to + */ + map (callbackfn: (value: T, index: number, array: Array) => U, thisArg?: any): Array { + return this.toArray().map(callbackfn, thisArg); + } +} diff --git a/packages/types/src/codec/createType.spec.ts b/packages/types/src/codec/createType.spec.ts new file mode 100644 index 0000000000..1c80400ae3 --- /dev/null +++ b/packages/types/src/codec/createType.spec.ts @@ -0,0 +1,227 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import createType, { TypeDefInfo, getTypeClass, getTypeDef, typeSplit } from './createType'; + +describe('typeSplit', () => { + it('splits simple types into an array', () => { + expect( + typeSplit('Text, u32, u64') + ).toEqual(['Text', 'u32', 'u64']); + }); + + it('splits nested combinations', () => { + expect( + typeSplit('Text, (u32), Vec') + ).toEqual(['Text', '(u32)', 'Vec']); + }); + + it('keeps nested tuples together', () => { + expect( + typeSplit('Text, (u32, u128), Vec') + ).toEqual(['Text', '(u32, u128)', 'Vec']); + }); + + it('keeps nested vector tuples together', () => { + expect( + typeSplit('Text, (u32, u128), Vec<(u64, u32)>') + ).toEqual(['Text', '(u32, u128)', 'Vec<(u64, u32)>']); + }); + + it('allows for deep nesting', () => { + expect( + typeSplit('Text, (u32, (u128, u8)), Vec<(u64, (u32, u32))>') + ).toEqual(['Text', '(u32, (u128, u8))', 'Vec<(u64, (u32, u32))>']); + }); + + it('checks for unclosed vec', () => { + expect( + () => typeSplit('Text, Vec { + expect( + () => typeSplit('Text, (u64, u32') + ).toThrow(/Invalid defintion/); + }); +}); + +describe('getTypeValue', () => { + it('does not allow invalid tuples, end )', () => { + expect( + () => getTypeDef('(u64, u32') + ).toThrow(/Expected '\(' closing with '\)'/); + }); + + it('does not allow invalid vectors, end >', () => { + expect( + () => getTypeDef('Vec'/); + }); + + it('returns a type structure', () => { + expect( + getTypeDef('(u32, Compact, Vec, Option, (Text, Vec<(Bool, u128)>))') + ).toEqual({ + info: TypeDefInfo.Tuple, + type: '(u32, Compact, Vec, Option, (Text, Vec<(Bool, u128)>))', + sub: [ + { + info: TypeDefInfo.Plain, + type: 'u32' + }, + { + info: TypeDefInfo.Compact, + type: 'Compact', + sub: { + info: TypeDefInfo.Plain, + type: 'u32' + } + }, + { + info: TypeDefInfo.Vector, + type: 'Vec', + sub: { + info: TypeDefInfo.Plain, + type: 'u64' + } + }, + { + info: TypeDefInfo.Option, + type: 'Option', + sub: { + info: TypeDefInfo.Plain, + type: 'u128' + } + }, + { + info: TypeDefInfo.Tuple, + type: '(Text, Vec<(Bool, u128)>)', + sub: [ + { + info: TypeDefInfo.Plain, + type: 'Text' + }, + { + info: TypeDefInfo.Vector, + type: 'Vec<(Bool, u128)>', + sub: { + info: TypeDefInfo.Tuple, + type: '(Bool, u128)', + sub: [ + { + info: TypeDefInfo.Plain, + type: 'Bool' + }, + { + info: TypeDefInfo.Plain, + type: 'u128' + } + ] + } + } + ] + } + ] + }); + }); + + it('returns a type structure (actual)', () => { + expect( + getTypeDef('Vec<(PropIndex, Proposal, AccountId)>') + ).toEqual({ + info: TypeDefInfo.Vector, + type: 'Vec<(PropIndex, Proposal, AccountId)>', + sub: { + info: TypeDefInfo.Tuple, + type: '(PropIndex, Proposal, AccountId)', + sub: [ + { + info: TypeDefInfo.Plain, + type: 'PropIndex' + }, + { + info: TypeDefInfo.Plain, + type: 'Proposal' + }, + { + info: TypeDefInfo.Plain, + type: 'AccountId' + } + ] + } + }); + }); + + it('returns an actual Struct', () => { + expect( + getTypeDef('{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}') + ).toEqual({ + info: TypeDefInfo.Struct, + type: '{"balance":"Balance","account_id":"AccountId","log":"(u64, Signature)"}', + sub: [ + { + info: TypeDefInfo.Plain, + name: 'balance', + type: 'Balance' + }, + { + info: TypeDefInfo.Plain, + name: 'account_id', + type: 'AccountId' + }, + { + info: TypeDefInfo.Tuple, + name: 'log', + type: '(u64, Signature)', + sub: [ + { + info: TypeDefInfo.Plain, + type: 'u64' + }, + { + info: TypeDefInfo.Plain, + type: 'Signature' + } + ] + } + ] + }); + }); +}); + +describe('getTypeClass', () => { + it('does not allow invalid types', () => { + expect( + () => getTypeClass('SomethingInvalid' as any) + ).toThrow(/determine type/); + }); +}); + +describe('createType', () => { + it('allows creation of a Struct', () => { + expect( + createType('{"balance":"Balance","index":"u32"}', { + balance: 1234, + index: '0x10' + }).toJSON() + ).toEqual({ + balance: 1234, + index: 16 + }); + }); + + it('allows creation of a Enum (simple)', () => { + expect( + createType('{"_enum": ["A", "B", "C"]}', 1).toJSON() + ).toEqual({ B: null }); + }); + + it('allows creation of a Enum (parametrised)', () => { + expect( + createType('{"_enum": {"A": null, "B": "u32", "C": null} }', 1).toJSON() + ).toEqual({ B: 0 }); + }); +}); diff --git a/packages/types/src/codec/createType.ts b/packages/types/src/codec/createType.ts new file mode 100644 index 0000000000..a4f3783153 --- /dev/null +++ b/packages/types/src/codec/createType.ts @@ -0,0 +1,218 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert } from '@plugnet/util'; + +import { Codec, Constructor } from '../types'; +import Text from '../primitive/Text'; +import EnumType from './EnumType'; +import Compact from './Compact'; +import Option from './Option'; +import Struct from './Struct'; +import Tuple from './Tuple'; +import UInt from './UInt'; +import Vector from './Vector'; +import getRegistry from './typeRegistry'; +import { Linkage } from './Linkage'; + +export enum TypeDefInfo { + Compact, + Enum, + Option, + Plain, + Struct, + Tuple, + Vector, + Linkage +} + +export type TypeDef = { + info: TypeDefInfo, + name?: string, + type: string, + sub?: TypeDef | Array +}; + +// safely split a string on ', ' while taking care of any nested occurences +export function typeSplit (type: string): Array { + let sDepth = 0; + let tDepth = 0; + let vDepth = 0; + let start = 0; + const result = []; + + for (let index = 0; index < type.length; index++) { + switch (type[index]) { + case ',': + // we are not nested, add the type + if (sDepth === 0 && tDepth === 0 && vDepth === 0) { + result.push(type.substr(start, index - start).trim()); + start = index + 1; + } + break; + + // inc struct depth, start found + case '{': sDepth++; break; + + // dec struct depth, end found + case '}': sDepth--; break; + + // inc tuple depth, start found + case '(': tDepth++; break; + + // dec tuple depth, end found + case ')': tDepth--; break; + + // inc compact/vec depth, start found + case '<': vDepth++; break; + + // dec compact/vec depth, end found + case '>': vDepth--; break; + + // normal character + default: break; + } + } + + assert(!sDepth && !tDepth && !vDepth, `Invalid defintion (missing terminators) found in ${type}`); + + // the final leg of the journey + result.push(type.substr(start, type.length - start).trim()); + + return result; +} + +export function getTypeDef (_type: Text | string, name?: string): TypeDef { + const type = _type.toString().trim(); + const value: TypeDef = { + info: TypeDefInfo.Plain, + name, + type + }; + let subType = ''; + + const startingWith = (type: string, start: string, end: string): boolean => { + if (type.substr(0, start.length) !== start) { + return false; + } + + assert(type[type.length - 1] === end, `Expected '${start}' closing with '${end}'`); + + subType = type.substr(start.length, type.length - start.length - 1); + + return true; + }; + + if (startingWith(type, '(', ')')) { + value.info = TypeDefInfo.Tuple; + value.sub = typeSplit(subType).map((inner) => getTypeDef(inner)); + } else if (startingWith(type, '{', '}')) { + const parsed = JSON.parse(type); + const keys = Object.keys(parsed); + + if (keys.length === 1 && keys[0] === '_enum') { + const details = parsed[keys[0]]; + + // not as pretty, but remain compatible with oo7 for both struct and Array types + value.sub = Array.isArray(details) + ? details.map((name) => ({ info: TypeDefInfo.Plain, name, type: 'Null' })) + : Object.keys(details).map((name) => ({ info: TypeDefInfo.Plain, name, type: details[name] || 'Null' })); + value.info = TypeDefInfo.Enum; + } else { + value.info = TypeDefInfo.Struct; + value.sub = keys.map((name) => getTypeDef(parsed[name], name)); + } + } else if (startingWith(type, 'Compact<', '>')) { + value.info = TypeDefInfo.Compact; + value.sub = getTypeDef(subType); + } else if (startingWith(type, 'Option<', '>')) { + value.info = TypeDefInfo.Option; + value.sub = getTypeDef(subType); + } else if (startingWith(type, 'Vec<', '>')) { + value.info = TypeDefInfo.Vector; + value.sub = getTypeDef(subType); + } else if (startingWith(type, 'Linkage<', '>')) { + value.info = TypeDefInfo.Linkage; + value.sub = getTypeDef(subType); + } + + return value; +} + +// Returns the type Class for construction +export function getTypeClass (value: TypeDef): Constructor { + const Type = getRegistry().get(value.type); + + if (Type) { + return Type; + } + + switch (value.info) { + case TypeDefInfo.Compact: + assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Compact'); + + return Compact.with( + getTypeClass(value.sub as TypeDef) as Constructor + ); + case TypeDefInfo.Enum: + assert(value.sub && Array.isArray(value.sub), 'Expected subtype for Enum'); + + return EnumType.with( + (value.sub as Array).reduce((result, sub, index) => { + result[sub.name as string] = getTypeClass(sub); + + return result; + }, {} as { [index: string]: Constructor }) + ); + case TypeDefInfo.Option: + assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Option'); + + return Option.with( + getTypeClass(value.sub as TypeDef) + ); + case TypeDefInfo.Struct: + assert(Array.isArray(value.sub), 'Expected nested subtypes for Struct'); + + return Struct.with( + (value.sub as Array).reduce((result, sub) => { + result[sub.name as string] = getTypeClass(sub); + + return result; + }, {} as { [index: string]: Constructor }) + ); + case TypeDefInfo.Tuple: + assert(Array.isArray(value.sub), 'Expected nested subtypes for Tuple'); + + return Tuple.with( + (value.sub as Array).map(getTypeClass) + ); + case TypeDefInfo.Vector: + assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Vector'); + + return Vector.with( + getTypeClass(value.sub as TypeDef) + ); + case TypeDefInfo.Linkage: + assert(value.sub && !Array.isArray(value.sub), 'Expected subtype for Linkage'); + return Linkage.withKey( + getTypeClass(value.sub as TypeDef) + ); + } + + throw new Error(`Unable to determine type from '${value.type}'`); +} + +export function createClass (type: Text | string): Constructor { + return getTypeClass( + getTypeDef(type) + ); +} + +export default function createType (type: Text | string, value?: any): Codec { + // l.debug(() => ['createType', { type, value }]); + + const Type = createClass(type); + + return new Type(value); +} diff --git a/packages/types/src/codec/index.ts b/packages/types/src/codec/index.ts new file mode 100644 index 0000000000..d838a9e49e --- /dev/null +++ b/packages/types/src/codec/index.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE We are not exporting everything here. These _should_ be enough to use the +// actual interfaces from a "create-a-working-coder" perspective. If not, we should +// expand with slight care (for instance, Length is really only used internally to +// others, so there _should_ not be need for direct use) + +// These are the base codec types, generally used for construction +export { default as Compact } from './Compact'; +export { default as Enum } from './Enum'; +export { default as EnumType } from './EnumType'; +export { default as Option } from './Option'; +export { default as Set } from './Set'; +export { default as Struct } from './Struct'; +export { default as Tuple } from './Tuple'; +export { default as Vector } from './Vector'; + +// Convenience base classes, used as "anything of this type" bases +export { default as U8a } from './U8a'; +export { default as UInt } from './UInt'; + +// Type management helper functions +export { default as createType, createClass, getTypeClass, getTypeDef, TypeDef, TypeDefInfo } from './createType'; +export { default as getTypeRegistry } from './typeRegistry'; diff --git a/packages/types/src/codec/typeRegistry.ts b/packages/types/src/codec/typeRegistry.ts new file mode 100644 index 0000000000..31b3f3e241 --- /dev/null +++ b/packages/types/src/codec/typeRegistry.ts @@ -0,0 +1,73 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isFunction, isString, isUndefined } from '@plugnet/util'; + +import { Constructor, RegistryTypes } from '../types'; +import { createClass } from './createType'; + +export class TypeRegistry { + static readonly defaultRegistry: TypeRegistry = new TypeRegistry(); + + private _registry: Map = new Map(); + + register (type: Constructor | RegistryTypes): void; + register (name: string, type: Constructor): void; + register (arg1: string | Constructor | RegistryTypes, arg2?: Constructor): void { + if (isString(arg1)) { + const name = arg1; + const type = arg2!; + + this._registry.set(name, type); + } else if (isFunction(arg1)) { + const name = arg1.name; + const type = arg1; + + this._registry.set(name, type); + } else { + this.registerObject(arg1); + } + } + + private registerObject (obj: RegistryTypes, overwrite: boolean = true) { + Object.entries(obj).forEach(([name, type]) => { + if (overwrite || !this.get(name)) { + if (isString(type)) { + this._registry.set(name, createClass(type)); + } else if (isFunction(type)) { + // This _looks_ a bit funny, but `typeof Clazz === 'function' + this._registry.set(name, type); + } else { + this._registry.set(name, createClass(JSON.stringify(type))); + } + } + }); + } + + get (name: string): Constructor | undefined { + return this._registry.get(name); + } + + getOrThrow (name: string, msg?: string): Constructor { + const type = this.get(name); + if (isUndefined(type)) { + throw new Error(msg || `type ${name} not found`); + } + + return type; + } +} + +let defaultRegistry: TypeRegistry; + +export default function getDefaultRegistry () { + if (!defaultRegistry) { + const defaultTypes = require('../index.types'); + + defaultRegistry = new TypeRegistry(); + defaultRegistry.register({ ...defaultTypes }); + } + + return defaultRegistry; +} diff --git a/packages/types/src/codec/utils/compareArray.spec.ts b/packages/types/src/codec/utils/compareArray.spec.ts new file mode 100644 index 0000000000..d6b74ecc75 --- /dev/null +++ b/packages/types/src/codec/utils/compareArray.spec.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../../primitive/U32'; +import compareArray from './compareArray'; + +describe('compareArray', () => { + const a = [new U32(123), new U32(456), new U32(789)]; + + it('returns false when second param is a non-array', () => { + expect( + compareArray(a, 123) + ).toBe(false); + }); + + it('compares array of codec agains primitive', () => { + expect( + compareArray(a, [123, 456, 789]) + ).toBe(true); + }); + + it('compares array of codec agains codec', () => { + expect( + compareArray(a, [new U32(123), new U32(456), new U32(789)]) + ).toBe(true); + }); + + it('compares primitive against primitive', () => { + expect( + compareArray( + [123, 456], [123, 456] + ) + ).toBe(true); + }); + + it('returns false when lengths are not matching', () => { + expect( + compareArray(a, [123]) + ).toBe(false); + }); + + it('returns false when value mismatches', () => { + expect( + compareArray(a, [123, 456, 999]) + ).toBe(false); + }); +}); diff --git a/packages/types/src/codec/utils/compareArray.ts b/packages/types/src/codec/utils/compareArray.ts new file mode 100644 index 0000000000..f9f9569448 --- /dev/null +++ b/packages/types/src/codec/utils/compareArray.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isFunction, isUndefined } from '@plugnet/util'; + +// NOTE These are used internally and when comparing objects, expects that +// when the second is an Array that the first has to be as well +export default function compareArray (a: Array, b?: any): boolean { + if (Array.isArray(b)) { + return (a.length === b.length) && isUndefined( + a.find((value, index) => + isFunction(value.eq) + ? !value.eq(b[index]) + : value !== b[index] + ) + ); + } + + return false; +} diff --git a/packages/types/src/codec/utils/compareMap.spec.ts b/packages/types/src/codec/utils/compareMap.spec.ts new file mode 100644 index 0000000000..332b8af0b8 --- /dev/null +++ b/packages/types/src/codec/utils/compareMap.spec.ts @@ -0,0 +1,81 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec } from '../../types'; +import U32 from '../../primitive/U32'; +import compareMap from './compareMap'; + +describe('compareMap', () => { + const a = new Map([ + ['decimals', new U32(15)], + ['missing', new U32(10)], + ['foobar', new U32(5)] + ]); + const b = new Map([ + ['decimals', 15], + ['missing', 10], + ['foobar', 5] + ]); + const c = new Map([ + ['decimals', new U32(15)], + ['missing', new U32(10)], + ['foobar', new U32(5)] + ]); + + it('compares Map against Object', () => { + expect( + compareMap(a, { + decimals: 15, + foobar: 5, + missing: 10 + }) + ).toBe(true); + }); + + it('compares Map against entries array', () => { + expect( + compareMap(b, [ + ['missing', 10], ['decimals', 15], ['foobar', 5] + ]) + ).toBe(true); + }); + + it('compares between 2 maps', () => { + expect(compareMap(a, b)).toBe(true); + }); + + it('compares between 2 maps (both codec)', () => { + expect(compareMap(a, c)).toBe(true); + }); + + it('returns false when second param is a non-map, non-array, non-object', () => { + expect( + compareMap(a, 123) + ).toBe(false); + }); + + it('returns false when second param is a array with non-entries', () => { + expect( + compareMap(a, [123, 456, 789]) + ).toBe(false); + }); + + it('returns false when second param is a array with non-entries (only key)', () => { + expect( + compareMap(a, [[123], [456], [789]]) + ).toBe(false); + }); + + it('returns false when properties are missing', () => { + expect( + compareMap(a, [['decimals', 15], ['wrong', 10], ['foobar', 5]]) + ).toBe(false); + }); + + it('returns false when lengths do not match', () => { + expect( + compareMap(a, [['decimals', 15]]) + ).toBe(false); + }); +}); diff --git a/packages/types/src/codec/utils/compareMap.ts b/packages/types/src/codec/utils/compareMap.ts new file mode 100644 index 0000000000..a5c9956504 --- /dev/null +++ b/packages/types/src/codec/utils/compareMap.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isFunction, isObject, isUndefined } from '@plugnet/util'; + +// NOTE These are used internally and when comparing objects, expects that +// when the second is an Map that the first has to be as well +export default function compareMap (a: Map, b?: any): boolean { + if (Array.isArray(b)) { + return (a.size === b.length) && isUndefined( + b.find((entry) => { + if (!Array.isArray(entry) || entry.length !== 2) { + return true; + } + + const value = a.get(entry[0]); + + return isUndefined(value) || ( + // Codec has .eq, use it here + isFunction(value.eq) + ? !value.eq(entry[1]) + : value !== entry[1] + ); + }) + ); + } else if (b instanceof Map) { + return compareMap(a, [...b.entries()]); + } else if (isObject(b)) { + return compareMap(a, Object.entries(b)); + } + + return false; +} diff --git a/packages/types/src/codec/utils/decodeU8a.ts b/packages/types/src/codec/utils/decodeU8a.ts new file mode 100644 index 0000000000..50e8629f87 --- /dev/null +++ b/packages/types/src/codec/utils/decodeU8a.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec, Constructor } from '../../types'; + +/** + * Given an u8a, and an array of Type constructors, decode the u8a against the + * types, and return an array of decoded values. + * + * @param u8a - The u8a to decode. + * @param types - The array of Constructor to decode the U8a against. + */ +export default function decodeU8a (u8a: Uint8Array, _types: Constructor[] | { [index: string]: Constructor }): Codec[] { + const types = Array.isArray(_types) + ? _types + : Object.values(_types); + + if (!types.length) { + return []; + } + + const Type = types[0]; + const value = new Type(u8a); + + return [value].concat(decodeU8a(u8a.subarray(value.encodedLength), types.slice(1))); +} diff --git a/packages/types/src/codec/utils/index.ts b/packages/types/src/codec/utils/index.ts new file mode 100644 index 0000000000..94fe457179 --- /dev/null +++ b/packages/types/src/codec/utils/index.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { default as compareArray } from './compareArray'; +export { default as compareMap } from './compareMap'; +export { default as decodeU8a } from './decodeU8a'; diff --git a/packages/types/src/index.spec.ts b/packages/types/src/index.spec.ts new file mode 100644 index 0000000000..5ff00461a9 --- /dev/null +++ b/packages/types/src/index.spec.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Constructor } from './types'; + +import extrinsics from '@plugnet/extrinsics/static'; + +import * as Classes from './index.types'; + +const Types = Classes as { [index: string]: Constructor }; + +describe('types', () => { + describe('default creation', () => { + Object.keys(Types).forEach((name) => { + it(`creates an empty ${name}`, () => { + const constructFn = () => + new Types[name](); + + if (name === 'Origin') { + expect(constructFn).toThrow(); + } else { + expect(constructFn).not.toThrow(); + } + }); + }); + }); + + describe('default creation (empty Bytes)', () => { + (Types.Method as any).injectMethods(extrinsics); + + Object.keys(Types).forEach((name) => { + it(`creates an empty ${name} (from empty bytes)`, () => { + const constructFn = () => + new Types[name](new Types.Bytes()); + + if (name === 'Origin') { + expect(constructFn).toThrow(); + } else { + expect(constructFn).not.toThrow(); + } + }); + }); + }); +}); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000000..eab9ce5e3b --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE When adding any types here, we need to update the coumentation links as +// well - /docs/SUMMARY.md as well as ../README.md + +import { assertSingletonPackage } from '@plugnet/util'; + +assertSingletonPackage('@polkadot/types'); + +export { default as ContractAbi } from './ContractAbi'; +export * from './codec'; +export * from './index.types'; diff --git a/packages/types/src/index.types.ts b/packages/types/src/index.types.ts new file mode 100644 index 0000000000..4aae7dcb5b --- /dev/null +++ b/packages/types/src/index.types.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE When adding any types here, we need to update the coumentation links as +// well - /docs/SUMMARY.md as well as ../README.md + +/** + * @summary Type definitions that are used in the system + */ +export { default as Metadata } from './Metadata'; +export * from './primitive'; +export * from './rpc'; +export * from './type'; diff --git a/packages/types/src/json/AccountIdVec.001.json b/packages/types/src/json/AccountIdVec.001.json new file mode 100644 index 0000000000..f7db57df75 --- /dev/null +++ b/packages/types/src/json/AccountIdVec.001.json @@ -0,0 +1,16 @@ +{ + "jsonrpc": "2.0", + "method": "state_storage", + "params": { + "result": { + "block": "0x3265584ff8e6a66cb6c334cdaf8bb6938b49b96e644b5bacc0d06f2b13f75230", + "changes": [ + [ + "0x329586a7b97f2ac15f6c26a02c3c5621", + "0x2082c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf54de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e758d67e0f9be62dce75adbb005e8097de5c45f32b1ba7264717df2db4ae9f276e8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081ca070532405ebf20fba389cbabfe1885cc134ee18028d488714eae621b47baf9d227cee94fa4e88d8d25abe706f15aca03b1d753d433f5ef9aa9ad1bcf5e5b81e040c8dc048a6d129803caa851c4c9633610068e4ef9eaa0bfbf40dfbfd43d9222347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140" + ] + ] + }, + "subscription": 43614 + } +} diff --git a/packages/types/src/json/EventRecord.001.json b/packages/types/src/json/EventRecord.001.json new file mode 100644 index 0000000000..85d616db93 --- /dev/null +++ b/packages/types/src/json/EventRecord.001.json @@ -0,0 +1,16 @@ +{ + "jsonrpc": "2.0", + "method": "state_storage", + "params": { + "result": { + "block": "0x4c51fb36c859f9d2554ef0dadf53351c343f2746221a963d630bfe396728842d", + "changes": [ + [ + "0xcc956bdb7605e3547539f321ac2bc95c", + "0x0400000000000000" + ] + ] + }, + "subscription": 14 + } +} diff --git a/packages/types/src/json/EventRecord.002.json b/packages/types/src/json/EventRecord.002.json new file mode 100644 index 0000000000..65d5236f69 --- /dev/null +++ b/packages/types/src/json/EventRecord.002.json @@ -0,0 +1,16 @@ +{ + "jsonrpc": "2.0", + "method": "state_storage", + "params": { + "result": { + "block": "0xa7f1ac9f18dded8e5825205d3e5260cf53ba01f629d9ebc1b80601b45ed8c358", + "changes": [ + [ + "0xcc956bdb7605e3547539f321ac2bc95c", + "0x10000000000000000001000000010037e027d776cd005c12bcf6722421374a9037167a0ceaf918f341c4ad68d54e59070000000000010000000102d2de7394ae047a5502ad9adb9cc69ff6fe484033bfce874d775da947487cd83237e027d776cd005c12bcf6722421374a9037167a0ceaf918f341c4ad68d54e59e80300000000000000000000000000000000000000000000000000000000000000010000000000" + ] + ] + }, + "subscription": 19 + } +} diff --git a/packages/types/src/json/EventRecord.003.json b/packages/types/src/json/EventRecord.003.json new file mode 100644 index 0000000000..b41b12489c --- /dev/null +++ b/packages/types/src/json/EventRecord.003.json @@ -0,0 +1,16 @@ +{ + "jsonrpc": "2.0", + "method": "state_storage", + "params": { + "result": { + "block": "0xa7f1ac9f18dded8e5825205d3e5260cf53ba01f629d9ebc1b80601b45ed8c358", + "changes": [ + [ + "0xcc956bdb7605e3547539f321ac2bc95c", + "0x140000000000000000010000000203306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc204a83f1c09be797bc3d9adce29818368b276a84e6b545ced492c25c948978d7f83930000000000000000000000000000000000000000000000000000000000000000100000000000103006400000000000000010d000100000001000000000000000000000000000000" + ] + ] + }, + "subscription": 19 + } +} diff --git a/packages/types/src/json/ExtrinsicStatus.001.json b/packages/types/src/json/ExtrinsicStatus.001.json new file mode 100644 index 0000000000..59e486bc00 --- /dev/null +++ b/packages/types/src/json/ExtrinsicStatus.001.json @@ -0,0 +1,10 @@ +{ + "jsonrpc": "2.0", + "method": "author_extrinsicUpdate", + "params": { + "result": { + "finalised": "0xc465b92a72b1d20918d64cd4effa70c2bb58b53a3f8c24c3ac8fd8f465f059b4" + }, + "subscription": 9 + } +} \ No newline at end of file diff --git a/packages/types/src/json/Header.001.json b/packages/types/src/json/Header.001.json new file mode 100644 index 0000000000..d2ac96dac6 --- /dev/null +++ b/packages/types/src/json/Header.001.json @@ -0,0 +1,13 @@ +{ + "jsonrpc": "2.0", + "result": { + "digest": { + "logs": [] + }, + "extrinsicsRoot": "0x45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0", + "number": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "stateRoot": "0x294c3470ae3be7555240b9d034ec19c3715ba2c3f20b92441f8cea0cab66ab56" + }, + "id": 1 +} diff --git a/packages/types/src/json/Header.002.json b/packages/types/src/json/Header.002.json new file mode 100644 index 0000000000..06cbb31dc8 --- /dev/null +++ b/packages/types/src/json/Header.002.json @@ -0,0 +1,20 @@ +{ + "jsonrpc": "2.0", + "result": { + "digest": { + "logs": [ + { + "Seal": [ + 256804359, + "0x2c0e0ee8a5d1a073da3f8db002b8eaebcd7b1a7eb25662e09f9ef0ac58d96bf17896256cd5f0bc672f96aab49fe5163ca5cb4c1a0f047dc39e89cd4a9eb2ea05" + ] + } + ] + }, + "extrinsicsRoot": "0x1da0d30001d07991baef3c5c65234c2e88bec659dc0d7138a7e7267e2bacbeb7", + "number": "0xb66", + "parentHash": "0xb58e4d84c2e0db1cdc8c8830aa719015f8c855f29a0dff2b158a160f29f73ba0", + "stateRoot": "0x22f6f5b6992ab87ea5f52b2732ed9e22a88643ed3a97251986857633687fbc54" + }, + "id": 1 +} diff --git a/packages/types/src/json/RuntimeVersion.001.json b/packages/types/src/json/RuntimeVersion.001.json new file mode 100644 index 0000000000..553b5bc7d3 --- /dev/null +++ b/packages/types/src/json/RuntimeVersion.001.json @@ -0,0 +1,39 @@ +{ + "jsonrpc": "2.0", + "result": { + "apis": [ + [ + [ + 105, + 110, + 104, + 101, + 114, + 101, + 110, + 116 + ], + 1 + ], + [ + [ + 118, + 97, + 108, + 105, + 100, + 97, + 116, + 120 + ], + 1 + ] + ], + "authoring_version": 1, + "impl_name": "substrate-node", + "impl_version": 0, + "spec_name": "node", + "spec_version": 1 + }, + "id": 1 +} \ No newline at end of file diff --git a/packages/types/src/json/SignedBlock.001.json b/packages/types/src/json/SignedBlock.001.json new file mode 100644 index 0000000000..9b484da41a --- /dev/null +++ b/packages/types/src/json/SignedBlock.001.json @@ -0,0 +1,43 @@ +{ + "jsonrpc": "2.0", + "result": { + "block": { + "extrinsics": [ + "0x0102004629c35b00000000", + "0x8110f8e1ebdd3cdef7423d24fe68f3863945ea21c190907d7f3394ddf153f633c77b894af36f7a36e7ec767f4593bfd8d084b66d805605905054327a08ff604187080100000000000000270e0100ff0e6422725af0e9aede3bef6eba77bc87afadf60b9735057ae93801f4c472ad7b00407a10f35a00000000000000000000" + ], + "header": { + "digest": { + "logs": [] + }, + "extrinsicsRoot": "0x79e749f5b04d6fde65503874daf1ca2a95509dfada574565fb81c11af236397c", + "number": 206053, + "parentHash": "0x6799fcd718579b07900bde8e14e2a1bde1c386f4539ef9cbb4407eebf5cb4857", + "stateRoot": "0xbfe02f7c26e5dad1226eccb1f4ef5306312602f7e1edbb9be50e8b87d9c7840b" + } + }, + "justification": { + "hash": "0xfab4bb3a8b0a072d3d09858dc865ad2750b2e708536b6dacc89fad369eba781a", + "round_number": 0, + "signatures": [ + [ + "0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c", + "0x84e343ddfd126d7c7a5c8feeac33dae947a08e4bee7b94080448fd31ae00f3a5e1a3e916926535976671ee61c430c215dbda1a781a7692a6ffc8f955b9d2d603" + ], + [ + "0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5", + "0x67089e8cbf4fec1c362de972c1430c468f572ceba0a26ca1ed4782155efc3ae2156cef02fb57f0012fe7766d932d2ccde9fd8d48bb4c869d7461079b66c1f002" + ], + [ + "0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5", + "0x9822441c8196fe9737e8d1aa49f4c0da046c15eba695fa89f98e2736fcc747e5ae88720fb554cc509d1e405e04194fda10a956eb85c76bde057ba01ffeef8507" + ], + [ + "0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7", + "0x1753981ad2dd67d18ca7026b2b2e8c3e0071da561eda8e3532a5b4a713415248f1df6ef32b32136ba2cb75cd4b4165aa9e1abe259b533046fb199e41b67f9406" + ] + ] + } + }, + "id": 8 +} \ No newline at end of file diff --git a/packages/types/src/json/SignedBlock.002.json b/packages/types/src/json/SignedBlock.002.json new file mode 100644 index 0000000000..ba8e20502e --- /dev/null +++ b/packages/types/src/json/SignedBlock.002.json @@ -0,0 +1,58 @@ +{ + "jsonrpc": "2.0", + "result": { + "block": { + "extrinsics": [ + "0x010200b0b1c85b00000000" + ], + "header": { + "digest": { + "logs": [ + { + "AuthoritiesChange": [ + "0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c", + "0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7", + "0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5", + "0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5", + "0x040c8dc048a6d129803caa851c4c9633610068e4ef9eaa0bfbf40dfbfd43d922", + "0x2347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140", + "0x673bda1eda2aa719d707473163d45300819f2da9fe6fdad6d74ce222ada2665a" + ] + } + ] + }, + "extrinsicsRoot": "0x295fe0f63daaa9552f497f6c4afcbab59ca5c681da9219907f188c765b73fb92", + "number": 262560, + "parentHash": "0x99b5b6d28a18c1fee26aa18a7f15b5ab20d416bc70319b4c3b3a7ae5a98284ea", + "stateRoot": "0x24bab7fe0fee187a57bd0029ba151e81a0bae0fea96ad9f85c579c1ba9d88948" + } + }, + "justification": { + "hash": "0x44bc10702350e3548eb8ea52354e8e07a5646e108f0aceef9488131f6f929f3b", + "round_number": 0, + "signatures": [ + [ + "0x063d7787ebca768b7445dfebe7d62cbb1625ff4dba288ea34488da266dd6dca5", + "0xe551027d14a2107328685f14a6e6b6a217d0e587ed40b55e3a356f4d02468fb968d9d1dd9d0c7026b712bf4d2eac3bc3ff9eeb2c9560a40575f41df69e769804" + ], + [ + "0x8101764f45778d4980dadaceee6e8af2517d3ab91ac9bec9cd1714fa5994081c", + "0xf303671e97a9b1811d3c0e1b87ebeb89f340ccc5d0d634c9596ca4764943561304df268498c133e88ab3a859d86821d85ad48ac53c54df9ec85e2a515adb0b00" + ], + [ + "0x82c39b31a2b79a90f8e66e7a77fdb85a4ed5517f2ae39f6a80565e8ecae85cf5", + "0xb2e326eb1ea3a803e6f331cbf2da8add553f08450a14fe1a26ba31039e3e1a70c6dcffd3361b94b973cdc7422f9e9b99edaea98117dda35275e6a36e6c54e30d" + ], + [ + "0x4de37a07567ebcbf8c64568428a835269a566723687058e017b6d69db00a77e7", + "0x1f739aa8cfcaa81362e3eb9c574844df40e3db009676d6065a7b5662d5e00b1385526257ba43a43cf84e7873354e3919e0dc2ac85a0426b43b3f37d311f64907" + ], + [ + "0x2347812ef77d9fd3cca1673e1b2bde54da96fddcf79d92832b1e2a819724f140", + "0x01370b87621008e9ee55476dc0c4246862aed787b94c27105eaba9c09e197882c6f40436ce2f45f0dbae03ff81af117bdaabc18f627b72aa6318326f6a067b05" + ] + ] + } + }, + "id": 8 +} \ No newline at end of file diff --git a/packages/types/src/json/StorageChangeSet.001.json b/packages/types/src/json/StorageChangeSet.001.json new file mode 100644 index 0000000000..802cb4556c --- /dev/null +++ b/packages/types/src/json/StorageChangeSet.001.json @@ -0,0 +1,16 @@ +{ + "jsonrpc": "2.0", + "method": "state_storage", + "params": { + "result": { + "block": "0x2ad8077937e9a5ceb2e0d57c95b95a6a9edcedc4fb1f14e3bc13245e223a569d", + "changes": [ + [ + "0x54bdbdb5e438d574dd4da05ee6131cee", + "0x914589504e470d0a1a0a0000000d49484452000000e1000000e00803000000c231f1ed00000090504c5445ffffff000000f6f6f6fcfcfcedededf9f9f9f0f0f0eaeaeab8b8b8e6e6e6f3f3f3cacacadfdfdfdbdbdbd6d6d6e2e2e26a6a6aa5a5a54545455b5b5bafafaf6464644f4f4f969696555555888888929292c4c4c4353535858585bababa7878781e1e1e414141252525a9a9a99e9e9e1313132c2c2c7d7d7d0d0d0d6060603333332222226969697272722b2b2b424242d27457990000108f49444154789ced5d6b57e23c179d82088a302015a40882e26d9479feffbf7b5b2ed2669f5bda14e65dcbfd69d690d69c2639977d4e925fbf7ef0831ffcfad56c5d7567cfc9fcf1e3ebed6d9a4c86f16cd6eddf5c37cfddb100687787f3fb8788c3ea6bd2e935ceddc9b2b8e826ffb1a215311876afcfdd5d4f5cc683e577ff9f06493ceb5f5d375aade616ad46a3dd4fa7edf4717c94f2cfa47fee5e9bd19e1ca6e5fb68b2b8921b37faf1e3ea20e5e8f9f2345dac827eb217ef69be689b9fea3ddfed85bceffcd3cbb29dece7e6a66397ee80def063f7f0ddac86ae05c16cb7aa96d3f20b6ab6d90939bf09d8af4068ac5f767daba82f9addc7ed7b6e1761fa150abfdfb6ddfa0833bf3ab7dbb7ad83bc2c08faa36d8f2617c1de7835dfbdf1df707afaf759675e43cfaa78abb416e797b1bd5580e36e0daf5efcfd07646c4db736ac2e6f64f670eef5186fb55e1de377c0ec33fd0b9fe7f2e7fadb2fdca9f9af0cb33ff2768ea9daf8cafef4672bfd67bbdf194e92791af90d9f3b7dc511f5c67625bcd439516874f611c1f4ee18441cb0dc0cfb213f7a3bd3d69b56c037ea687e10515e111f7140177a91bdf194eeea258e1b8571381bd9cc1cd6c1c986716a926f8bc43fc660d0cd5e77a2d5b8b10b987df8df81fe6c330b21e7815e26a1ef25df56c650e398adc6bfa15535a0e32d60c02fdf7e8d6a37c0eb3202a608a573b2286d1ae85d2492920246d15d200399cda14d985751782b2d60144c0f667ae02eccab10832a02a6f16c985e64c67814e6552ebeaa09982ad530fd68a51eff7d98571531aa2a60eac506724a6ed3982dcc9bf2d03d51033e03a52746358818600433bc07b2fea3e013359080a988e14631a88841a6e80e4f810c632ae247983765082860aa6e02756a144c39fb06132a42f56b15458f61de54c993a110881e6c3e04f25187a1058ca240ec60eb25c8d7f28f070d08a46d2ea200eccd751d0206739d7be9bbaae68356b548182c5e9c55d6cd1ea4931f42d166938ae162b72e01a3b7401266b6ba0aaff1529b84d191825b6f1edfe6c9643d7c8e3bb36ebf77d3f3ea63faaef2352ac12d610e4797ab07bf253e9d4c95fdb8ac80f5cdd10c47a38816d78b324ca268584ec066ad02e603bc3be13703d280b85c5016d81d05e4961bac772fe5715d321eae778ea6f83afe2d749cbc125771a9795af31ccd90f34626ee6f7e466e5c669eceeb97304f2fbeba3f7a79e7ed12f3f4aa7e01a365eeeffd967ed491ce81d85342506f75203f4e304ffde2a26514f93140b5c44c8082ebf6c7fdd5cb53e9fa062cab934818e5ff24b8367e44d3c86fe996ca129640c10305dde695cbf154362712d049d6bc3bbfbef84898057af64f52360fea8da2cb3c737ff65236ad28facfdaf604c6fe80226173effeeca56c86f6413cd910bacaa1edfeec47adda07f1740246cfc5bf0ca4895730bcb6aad378f7f287643d8ce3ce629662d1898763f7aff3789fbc0d46e3dbd5c3dfcf17577d14e15669b8bfaf7c246c5a93c3fb922e3780f170020a0e54b60da8c5d1216e741ebb0dbc48b9892d763e6834e08acc023ec03bf9afe3b604cfc647c26b1bc375481582357ab44a888be1896deb3a9310957a598c8d854e3fea3337e25a180544052828679855b0dc7d62e19e25c4381605397a2e33a92640971a4263509630a1bdd24babe8556d732c1d852cb24d9b62d98c144c6362e5d66de263f63bbac1c847beee6098f26ce84c5e48cd91728295e893066deacdf324b0aba96f2c12a27a17393b62d94001b20f05f3a8e99a824b0a5fc320205a0af9bbb88bfd17511ef8856d58f43526b21818babf1ad8295c05e04e6b12a2f1f4d98db85438fe62dd8cdb5d9d4245af49718588598aa18d0f3f91c8848da3d7dd0c891e566151b73c84a4f9429ded31883d991d76d88b27f7778d81433da679b394df898bc167257e8a6c865b41ea7a1c1a7f833975b06e0ea89815dc6f6a6eb048c4d4be1beab84ea1e2d6a0fba1ae5c6afe11e3eeb11245cf0d886e186f7955e1125f691252b68ee2db3d6ce28b10253ec38bddf1c61639e01002b704a0ba41cd140fc7460a30b0c6d23557a2078651007faac91e346148b5b47ba7339e91228c014cd315df5bccbeeb4348f3da544b7b8871cd1702509addfd7482f78d53030276c357c940723bf63871c5da0b2a5075d512107edfc060dc9045a6691852427bb09fb0054994a204278f3d55078770a54b485b2ebaad59c22e5bcf47bed7b500dc34c5c0575f855c66826e6c4e8136b8bd6474dad77d2fa74d4b0d219dad67dc5f9d9f38e081694b334d304de98ee3105a6a39e8508efb88e6d4d29c59889067dec1d5a6b4d1474d67617568230739fd3dccdb0f62861966f654b8d3f4926a84d6ca4291334a9d5dc0d620ea86991d0c6d0bd3941a1c1c0ccb1e1422c0cfc0da5c73c926ed20906393c1d5a6447083aacbc45a319e184fae5badfe2d198cb09ac19da684638c66cdb2958f0b89f883ecac5b1837d13bf1bfc4d0ec00d314b6d0208f2f7ae80730ba51e24a8c120ec9967c49b71b9b8126c008dc5233c6d937aca83dc258a0372395295f100caf757ec78053ca547c83e335c518d426619b24f779e5072cafa30af06526fe9feb9e487731ead7418b6cb8e25febcec2a2ad439adb5401cfba99e2539f2609d3e88488cb845e810a2bb4c508c8e073474bae738aab6073dd5e2902d2a737f9d402111f581c3676bf92b289c5c6ba8d297f497aadbbd2f29e2346a696da54dec7e433e23b98089b0151c52132f6e02ee52826f4332ca682ad99e0dcee6f98acfe1b618b6405ef467f478d8e349fa5688ca724f4822c8b847362f1b06ee916aee23b6edac3aa25434503a17e0f58a90f5b4a6ca644482e4b085d3a182da268494b55445262c1b0862d65a41419a578926e6876f0d3d13131f451605c7092de817235a4692684843c4db805e89a3de387f48cbe655162e89162edc3ba361c59b42634a022218892707f4cf56784454868d207225eac4742777def2623f20a3a012525a1912b1a1262ebba8692508be8607d670a85180e753f9848b6a0b9cf3aeafa48baae199690902c73c3704d3586a25f89a1e1d6f7013f57cd265212cad622229228ef94d6d78ca14c5c638cba9b90eeffaa89286a96aa1befc14948a8d9a2506c4a7a051fd8e97c589e6524d4c372371a6810935449f42b7a1ea3ae3ded0f6a503b498192502fe902b2628acb412ed6d0221f9c0087a0c64d8b697b9b27a524b49ccf204e52ad2c1da7d177410f8cae12439129445d427d13b93cd5353e17d99de3b27593a64a0c45e6660ca7aeaa128a9a54dd0b811edb71a4dc82c5a526214109ab7513065f42a2c9d4c815099adcca077f5ee66b3658b1a696d86550f77708cf4adee80e7868615e8a95f39bbc616844f5d5b2d940d9a423716c6ac8836bb8c01c42d2413c1d6545695b0bbba2ec2117ced1d0cb6130322c985bb0b4a277b4a4ba6adab126ab432170d2377aa0a62b3ee34e62d12492cbdeb46144f4ba849ca17e0a04ce7067d8212c13ccf30539c4a65322965227f959a0ab198245763d26f777210fd5236d93edcc2bc921e4b5b17e2017c64da02c5d45217cb60ea9d96c7b63a5d9cf3e641842b4a41029c347e063e929ed9dd836fdf0afe597a1be0a31fe263ea5ab8b782f72444b683bd78b27cad8b8c2b0eb1c4d15e1b3b8512295abdfe19d2eeb34eebe632d2dfb85f4aa42d401d4296cc048719e6e83195f3527a2f497754af51a6da4d8c889edbae65c40cd1627da2464230cce75379cc405cf2cc966ae39e2a6e984339606df3b03a7377cdb1f818121ed92813e62028c11155964309e34c0e97eaebd5ec884cf300ddda40fa3f65e3807daba1f9dfe725c7caf4f52ac55e2dc156849b6ba62dd72eb465f3a43cd11ae7a910ffadc5c4bd0b9e4c78e792d6b3d36818cf5b8c4871a18a21de53f8a5b70464ed33bfe133135b40032bee6f2869a80bf3e3d1e019b4d35120e8f301f0c41e9626696aad521e86848b1addb9698a6e20e44ab8464c44e3755092830a3a293e7c6c1c4345d4bceb3d122d2213bdd520b9c30f215193db73959afc43bac7259601e94db4797fa68443990a472a805311e68cd4b313d623f698f789866f415820687501974779ac2b78e652e158e2fe440ac6592acd3b681c0106a9cacfb49966e0326363cc07e44143e4bc687ca1e0954a41af70f6e89636f1bca6e4c5371ef16a8d2c9e84bc918c2dad58f41728dbe13cd7534c2df7ca827a1c4a84d7572be0a875daf06729f7112d1b79a8f219591178141114579cbf13d50c8868359619a1552896d9d5eb75d6d1851412a95b610fd6e344e96e25137842aac97897e029ad537a556a2a991d4de74059d1b2f1796eea7ce7ba9552747c0b3c43495027cfc98a6d3f681b5cc3dd5b554d89a8ff4c21948685361f700126cc6adbeee42caf908230befe571f21cf82b18600a9dc611b709086370b47f6ddb2645b35f835a0bb33bbcb5c001b71e530e1aedfb97b96db782c701b43023601079d50d4d0dd98d1dc05e1cb467ea96ff35bdc17ee70304fb30c5d90018bfa3fd045937c83b2884b5751e780c22d81ed7f5e70606d58cc7c1c8ae1d3d7c46fb29e0f64104aad035369c756a5d5f5f5e5e5e7ce3d2e734ee56efe680dfedf4d9d6b7e0d63dfb1e8308d19c6b8f03dd246781cfd9caf6415cc2b3ab6203bf2b55aa20f6b93482dd318b0083e7f004bec7dd97865991eef0d72e2238cbc54dce812e7fd391f8dddfe5717d07e8c0a2b2f1f9ae55d0f030a85b789c8b0c5faea8cc03ddb1a661e37b619de9c4803de0e155fed7d3dcf8def33bb62e83c75d4f10d515944db0dbb9443cf8db256baa2d034c8f3c65779285f85ce626440f8b81a4683e19e171226059643e6089c73c940dc4d557d28fe171576eb99bceb6d803c2a8dc3c5d56164043a7eccdb9e63c0d45e4e618b1bab5696680bd6ec03ac2639e8214b9a477dd6ecd7df93b31ed1c3f91b2cba524ea8d2f86d62b112878386f3850c7048189062d8b8ca52f7fc5a3cfdd64b8da8e933cd4ada304b29469a5086d651731c5cb9fdbd1dd5776ebe6344926473aa54683516111eee0b114259454753a263e67f1d1b027a32454bad15640469d96bbbf32070fef4d403d6446f6f9035cf21de4d663bfe8d488ccd407614970cb5509d4a16c9e82791386b33c74f81cff6fee56a91b5609b4b4737f2c60ca76cbe3beba1a3da261ce7d0b08ac4f47a980013d09db962105256f5ea6912a8765505729c8cdab01c3a874049f02bb11c6dd1832bceedb96701f5ec040a368d8d26f41aa459f6af0e6afecf91a162f15e29c6f5cbca61e442de1cab57cd39f09cbea732b73d51e6a62d25bfa41f92adeab3aca99b35d3ea65711c2bba9e62a67872bd415a96c11e2366b9f249883561655ebe77354428848a3346fb32df5b0277a4bc2834565b12aa752b7350201e2410d41a27edb69b905343266eb4fe588de82a67a629901b7befecdb6d62ba86babfeb5aa98fb10c5dbafba0ae41259d0b29cb9ae22313b26b3ccd9f0baafb33ac2105473d3b2bacc6cd467789240c18de1502203ee55ebd8da9ea0f478a28a8702c20c63da79296ebcd819e013d8080aad20446386bb673217de5cec3653f9e7e883e12284e1d8e1fd238967dd6e7f8f6e77b6de6f8f99d7961330e1c6721949150c821103e56534efcb2c81d1c9352889be473adc0be3d3d56e6ae8855b8f47dc9f498132b8b66f5db4617efef50758ac8289f7b93eaffe64f13bf128bde521ba0067470faed5f6c4c7a2c6ba8640e825a55dd68fcebf2fde0eedd89bb27aff5a77cfe15d57c04dbc5919a51b4f17276127eac04d3c1f4bdae7753cedfc3b66bd345a17ddce7afe38188cc6b7b7b7e3fbc160f3389fc48b6efbff65d5fde0073ff8818cff01007fd85457cad4050000000049454e44ae426082" + ] + ] + }, + "subscription": 36 + } +} diff --git a/packages/types/src/json/json.d.ts b/packages/types/src/json/json.d.ts new file mode 100644 index 0000000000..e9645d4b99 --- /dev/null +++ b/packages/types/src/json/json.d.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// Be able to import json in TS +// https://stackoverflow.com/questions/49996456/importing-json-file-in-typescript +declare module '*.json' { + const value: any; + export default value; +} diff --git a/packages/types/src/primitive/Bool.spec.ts b/packages/types/src/primitive/Bool.spec.ts new file mode 100644 index 0000000000..ad8a381232 --- /dev/null +++ b/packages/types/src/primitive/Bool.spec.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Bool from './Bool'; +import { CodecTo } from '../types'; + +describe('Bool', () => { + describe('decode', () => { + const testDecode = (type: string, input: Uint8Array | boolean | Boolean | Bool | number, expected: boolean) => + it(`can decode from ${type}`, () => { + expect(new Bool(input).toJSON()).toBe(expected); + }); + + testDecode('Bool', new Bool(true), true); + testDecode('Boolean', Boolean(true), true); + testDecode('boolean', true, true); + testDecode('number', 1, true); + testDecode('Uint8Array', Uint8Array.from([1]), true); + }); + + describe('encode', () => { + const testEncode = (to: CodecTo, expected: string | Uint8Array | boolean, value: boolean) => + it(`can encode ${to}`, () => { + expect(new Bool(value)[to]()).toEqual(expected); + }); + + testEncode('toJSON', true, true); + testEncode('toHex', '0x01', true); + testEncode('toString', 'true', true); + testEncode('toU8a', Uint8Array.from([1]), true); + testEncode('toU8a', Uint8Array.from([0]), false); + }); + + it('correctly encodes length', () => { + expect(new Bool(true).encodedLength).toEqual(1); + }); + + describe('utils', () => { + it('compares agains a boolean', () => { + expect(new Bool(true).eq(true)).toBe(true); + }); + + it('compares agains a Bool', () => { + expect(new Bool(false).eq(new Bool(false))).toBe(true); + }); + }); +}); diff --git a/packages/types/src/primitive/Bool.ts b/packages/types/src/primitive/Bool.ts new file mode 100644 index 0000000000..084dd8b5b8 --- /dev/null +++ b/packages/types/src/primitive/Bool.ts @@ -0,0 +1,85 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a, u8aToHex } from '@plugnet/util'; + +import { Codec } from '../types'; + +/** + * @name Bool + * @description + * Representation for a boolean value in the system. It extends the base JS `Boolean` class + * @noInheritDoc + */ +export default class Bool extends Boolean implements Codec { + constructor (value: Bool | Boolean | Uint8Array | boolean | number = false) { + super( + Bool.decodeBool(value) + ); + } + + private static decodeBool (value: any): boolean { + if (value instanceof Boolean) { + return value.valueOf(); + } else if (isU8a(value)) { + return value[0] === 1; + } + + return !!value; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return 1; + } + + /** + * @description Checks if the value is an empty value (always false) + */ + get isEmpty (): boolean { + return false; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return this.valueOf() === ( + other instanceof Boolean + ? other.valueOf() + : other + ); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.valueOf(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return `${this.toJSON()}`; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return new Uint8Array([this.valueOf() ? 1 : 0]); + } +} diff --git a/packages/types/src/primitive/Bytes.spec.ts b/packages/types/src/primitive/Bytes.spec.ts new file mode 100644 index 0000000000..a3f978eee2 --- /dev/null +++ b/packages/types/src/primitive/Bytes.spec.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Bytes from './Bytes'; +import StorageData from './StorageData'; + +describe('Bytes', () => { + const CODE = new Uint8Array([0x14, 0x3a, 0x63, 0x6f, 0x64, 0x65]); + + describe('construction', () => { + it('decodes when input is string', () => { + expect( + new Bytes(':code').toU8a() + ).toEqual(CODE); + }); + + it('decodes when hex is not length prefixed', () => { + expect( + new Bytes('0x3a636f6465').toU8a() + ).toEqual(CODE); + }); + + it('decodes from Array', () => { + expect( + new Bytes([0x3a, 0x63, 0x6f, 0x64, 0x65]).toU8a() + ).toEqual(CODE); + }); + + it('creates via storagedata (no prefix)', () => { + expect( + new Bytes( + new StorageData('0x3a636f6465') + ).toU8a() + ).toEqual(CODE); + }); + + it('creates via storagedata (with length prefix)', () => { + expect( + new Bytes( + new StorageData('0x143a636f6465') + ).toU8a() + ).toEqual(CODE); + }); + }); +}); diff --git a/packages/types/src/primitive/Bytes.ts b/packages/types/src/primitive/Bytes.ts new file mode 100644 index 0000000000..71f4ba4990 --- /dev/null +++ b/packages/types/src/primitive/Bytes.ts @@ -0,0 +1,74 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isString, isU8a, u8aToU8a } from '@plugnet/util'; + +import { AnyU8a } from '../types'; +import Compact from '../codec/Compact'; +import U8a from '../codec/U8a'; + +/** + * @name Bytes + * @description + * A Bytes wrapper for Vec. The significant difference between this and a normal Uint8Array + * is that this version allows for length-encoding. (i.e. it is a variable-item codec, the same + * as what is found in [[Text]] and [[Vector]]) + */ +export default class Bytes extends U8a { + constructor (value?: AnyU8a) { + super(Bytes.decodeBytes(value)); + } + + private static decodeBytes (value?: AnyU8a): Uint8Array | undefined { + // FIXME Cyclic dependency, however needed for the StoreageData check below. In a perfect + // world, we should probably be checking Bytes - however as a first step, check against + // StorageData to cater for the _specific_ problematic case + const StorageData = require('./StorageData').default; + + if (Array.isArray(value) || isString(value)) { + const u8a = u8aToU8a(value); + + return Bytes.decodeBytes( + Compact.addLengthPrefix(u8a) + ); + } else if (value instanceof StorageData) { + // Here we cater for the actual StorageData that _could_ have a length prefix. In the + // case of `:code` it is not added, for others it is + const u8a = value as Uint8Array; + const [offset, length] = Compact.decodeU8a(u8a); + + return u8a.length === length.addn(offset).toNumber() + ? u8a.subarray(offset) + : u8a; + } else if (value instanceof U8a) { + // This is required. In the case of a U8a we already have gotten rid of the length, + // i.e. new Bytes(new Bytes(...)) will work as expected + return value; + } else if (isU8a(value)) { + // handle all other Uint8Array inputs, these do have a length prefix + const [offset, length] = Compact.decodeU8a(value); + + return value.subarray(offset, offset + length.toNumber()); + } + + return value; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.length + Compact.encodeU8a(this.length).length; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return isBare + ? super.toU8a(isBare) + : Compact.addLengthPrefix(this); + } +} diff --git a/packages/types/src/primitive/Data.ts b/packages/types/src/primitive/Data.ts new file mode 100644 index 0000000000..5a58674477 --- /dev/null +++ b/packages/types/src/primitive/Data.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U8a from '../codec/U8a'; + +/** + * @name Data + * @description + * A raw data structure. It is just an encoding of a U8a, without any length encoding + */ +export default class Data extends U8a { +} diff --git a/packages/types/src/primitive/H160.ts b/packages/types/src/primitive/H160.ts new file mode 100644 index 0000000000..e960100640 --- /dev/null +++ b/packages/types/src/primitive/H160.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import U8aFixed from '../codec/U8aFixed'; + +/** + * @name H160 + * @description + * Hash containing 160 bits (20 bytes), typically used in blocks, extrinsics and + * as a sane default for fixed-length hash representations. + */ +export default class H160 extends U8aFixed { + constructor (value?: AnyU8a) { + super(value, 160); + } +} diff --git a/packages/types/src/primitive/H256.ts b/packages/types/src/primitive/H256.ts new file mode 100644 index 0000000000..124237c01c --- /dev/null +++ b/packages/types/src/primitive/H256.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import U8aFixed from '../codec/U8aFixed'; + +/** + * @name H256 + * @description + * Hash containing 256 bits (32 bytes), typically used in blocks, extrinsics and + * as a sane default for fixed-length hash representations. + */ +export default class H256 extends U8aFixed { + constructor (value?: AnyU8a) { + super(value, 256); + } +} diff --git a/packages/types/src/primitive/H512.ts b/packages/types/src/primitive/H512.ts new file mode 100644 index 0000000000..444fb984b8 --- /dev/null +++ b/packages/types/src/primitive/H512.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import U8aFixed from '../codec/U8aFixed'; + +/** + * @name H512 + * @description + * Hash containing 512 bits (64 bytes), typically used for signatures + */ +export default class H512 extends U8aFixed { + constructor (value?: AnyU8a) { + super(value, 512); + } +} diff --git a/packages/types/src/primitive/I128.ts b/packages/types/src/primitive/I128.ts new file mode 100644 index 0000000000..f964b96efa --- /dev/null +++ b/packages/types/src/primitive/I128.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I128 + * @description + * An 128-bit signed integer + */ +export default class I128 extends Int { + constructor (value?: AnyNumber) { + super(value, 128); + } +} diff --git a/packages/types/src/primitive/I16.ts b/packages/types/src/primitive/I16.ts new file mode 100644 index 0000000000..5d9592fb92 --- /dev/null +++ b/packages/types/src/primitive/I16.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I16 + * @description + * An 16-bit signed integer + */ +export default class I16 extends Int { + constructor (value?: AnyNumber) { + super(value, 16); + } +} diff --git a/packages/types/src/primitive/I256.ts b/packages/types/src/primitive/I256.ts new file mode 100644 index 0000000000..677697b4ec --- /dev/null +++ b/packages/types/src/primitive/I256.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I256 + * @description + * An 256-bit signed integer + */ +export default class I256 extends Int { + constructor (value?: AnyNumber) { + super(value, 256); + } +} diff --git a/packages/types/src/primitive/I32.ts b/packages/types/src/primitive/I32.ts new file mode 100644 index 0000000000..d17db6c733 --- /dev/null +++ b/packages/types/src/primitive/I32.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I32 + * @description + * An 32-bit signed integer + */ +export default class I32 extends Int { + constructor (value?: AnyNumber) { + super(value, 32); + } +} diff --git a/packages/types/src/primitive/I64.ts b/packages/types/src/primitive/I64.ts new file mode 100644 index 0000000000..de9ddd5c48 --- /dev/null +++ b/packages/types/src/primitive/I64.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I64 + * @description + * An 64-bit signed integer + */ +export default class I64 extends Int { + constructor (value?: AnyNumber) { + super(value, 64); + } +} diff --git a/packages/types/src/primitive/I8.ts b/packages/types/src/primitive/I8.ts new file mode 100644 index 0000000000..575edfa2d0 --- /dev/null +++ b/packages/types/src/primitive/I8.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Int from '../codec/Int'; + +/** + * @name I8 + * @description + * An 8-bit signed integer + */ +export default class I8 extends Int { + constructor (value?: AnyNumber) { + super(value, 8); + } +} diff --git a/packages/types/src/primitive/Method.spec.ts b/packages/types/src/primitive/Method.spec.ts new file mode 100644 index 0000000000..31606463d4 --- /dev/null +++ b/packages/types/src/primitive/Method.spec.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsics from '@plugnet/extrinsics/static'; + +import Method from './Method'; + +describe('Method', () => { + beforeEach(() => { + Method.injectMethods(extrinsics); + }); + + it('handles decoding correctly (bare)', () => { + expect( + new Method({ + args: [], + callIndex: [1, 2] + }).toU8a() + ).toEqual(new Uint8Array([1, 2, 0])); // array of 0 + }); + + it('handles creation from a hex value properly', () => { + expect( + new Method('0x0102').toU8a() + ).toEqual(new Uint8Array([1, 2, 0])); // array of 0 for consensus.noteOffline + }); + + describe('hasOrigin', () => { + const test = { + args: [], + callIndex: [1, 2] + }; + + it('is false with no arguments', () => { + expect( + new Method(test, { arguments: [] } as any).hasOrigin + ).toEqual(false); + }); + + it('is false with first argument as non-Origin', () => { + expect( + new Method(test, { arguments: [{ name: 'a', type: 'u32' }] } as any).hasOrigin + ).toEqual(false); + }); + + it('is false with first argument as non-Origin', () => { + expect( + new Method(test, { arguments: [{ name: 'a', type: 'Origin' }] } as any).hasOrigin + ).toEqual(true); + }); + }); +}); diff --git a/packages/types/src/primitive/Method.ts b/packages/types/src/primitive/Method.ts new file mode 100644 index 0000000000..9c3e698daf --- /dev/null +++ b/packages/types/src/primitive/Method.ts @@ -0,0 +1,229 @@ +// Copyright 2017-2019 @polkadot/extrinsics authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a, ArgsDef, Codec, IMethod } from '../types'; + +import { assert, isHex, isObject, isU8a, hexToU8a } from '@plugnet/util'; + +import { getTypeDef, getTypeClass } from '../codec/createType'; +import Struct from '../codec/Struct'; +import U8aFixed from '../codec/U8aFixed'; +import { FunctionMetadata, FunctionArgumentMetadata } from '../Metadata/v0/Modules'; + +interface DecodeMethodInput { + args: any; + callIndex: MethodIndex | Uint8Array; +} + +interface DecodedMethod extends DecodeMethodInput { + argsDef: ArgsDef; + meta: FunctionMetadata; +} + +export interface MethodFunction { + (...args: any[]): Method; + callIndex: Uint8Array; + meta: FunctionMetadata; + method: string; + section: string; + toJSON: () => any; +} + +export interface Methods { + [key: string]: MethodFunction; +} + +export interface ModulesWithMethods { + [key: string]: Methods; // Will hold modules returned by state_getMetadata +} + +const FN_UNKNOWN = { + method: 'unknown', + section: 'unknown' +} as MethodFunction; + +const injected: { [index: string]: MethodFunction } = {}; + +/** + * @name MethodIndex + * @description + * A wrapper around the `[sectionIndex, methodIndex]` value that uniquely identifies a method + */ +export class MethodIndex extends U8aFixed { + constructor (value?: AnyU8a) { + super(value, 16); + } +} + +/** + * @name Method + * @description + * Extrinsic function descriptor, as defined in + * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}. + */ +export default class Method extends Struct implements IMethod { + protected _meta: FunctionMetadata; + + constructor (value: any, meta?: FunctionMetadata) { + const decoded = Method.decodeMethod(value, meta); + + super({ + callIndex: MethodIndex, + args: Struct.with(decoded.argsDef) + }, decoded); + + this._meta = decoded.meta; + } + + /** + * Decode input to pass into constructor. + * + * @param value - Value to decode, one of: + * - hex + * - Uint8Array + * - {@see DecodeMethodInput} + * @param _meta - Metadata to use, so that `injectMethods` lookup is not + * necessary. + */ + private static decodeMethod (value: DecodedMethod | Uint8Array | string, _meta?: FunctionMetadata): DecodedMethod { + if (isHex(value)) { + return Method.decodeMethod(hexToU8a(value), _meta); + } else if (isU8a(value)) { + // The first 2 bytes are the callIndex + const callIndex = value.subarray(0, 2); + + // Find metadata with callIndex + const meta = _meta || Method.findFunction(callIndex).meta; + + return { + args: value.subarray(2), + argsDef: Method.getArgsDef(meta), + callIndex, + meta + }; + } else if (isObject(value) && value.callIndex && value.args) { + // destructure value, we only pass args/methodsIndex out + const { args, callIndex } = value; + + // Get the correct lookupIndex + const lookupIndex = callIndex instanceof MethodIndex + ? callIndex.toU8a() + : callIndex; + + // Find metadata with callIndex + const meta = _meta || Method.findFunction(lookupIndex).meta; + + return { + args, + argsDef: Method.getArgsDef(meta), + meta, + callIndex + }; + } + + console.error(`Method: cannot decode value '${value}' of type ${typeof value}`); + + return { + args: new Uint8Array(), + argsDef: {}, + meta: new FunctionMetadata(), + callIndex: new Uint8Array([255, 255]) + }; + } + + // If the extrinsic function has an argument of type `Origin`, we ignore it + static filterOrigin (meta?: FunctionMetadata): Array { + // FIXME should be `arg.type !== Origin`, but doesn't work... + return meta + ? meta.arguments.filter(({ type }) => + type.toString() !== 'Origin' + ) + : []; + } + + // We could only inject the meta (see injectMethods below) and then do a + // meta-only lookup via + // + // metadata.modules[callIndex[0]].module.call.functions[callIndex[1]] + // + // As a convenience helper though, we return the full constructor function, + // which includes the meta, name, section & actual interface for calling + static findFunction (callIndex: Uint8Array): MethodFunction { + assert(Object.keys(injected).length > 0, 'Calling Method.findFunction before extrinsics have been injected.'); + + return injected[callIndex.toString()] || FN_UNKNOWN; + } + + /** + * Get a mapping of `argument name -> argument type` for the function, from + * its metadata. + * + * @param meta - The function metadata used to get the definition. + */ + private static getArgsDef (meta: FunctionMetadata): ArgsDef { + return Method.filterOrigin(meta).reduce((result, { name, type }) => { + const Type = getTypeClass( + getTypeDef(type) + ); + result[name.toString()] = Type; + + return result; + }, {} as ArgsDef); + } + + // This is called/injected by the API on init, allowing a snapshot of + // the available system extrinsics to be used in lookups + static injectMethods (moduleMethods: ModulesWithMethods): void { + Object.values(moduleMethods).forEach((methods) => + Object.values(methods).forEach((method) => + injected[method.callIndex.toString()] = method + ) + ); + } + + /** + * @description The arguments for the function call + */ + get args (): Array { + // FIXME This should return a Struct instead of an Array + return [...(this.get('args') as Struct).values()]; + } + + /** + * @description Thge argument defintions + */ + get argsDef (): ArgsDef { + return Method.getArgsDef(this.meta); + } + + /** + * @description The encoded `[sectionIndex, methodIndex]` identifier + */ + get callIndex (): Uint8Array { + return (this.get('callIndex') as MethodIndex).toU8a(); + } + + /** + * @description The encoded data + */ + get data (): Uint8Array { + return (this.get('args') as Struct).toU8a(); + } + + /** + * @description `true` if the `Origin` type is on the method (extrinsic method) + */ + get hasOrigin (): boolean { + const firstArg = this.meta.arguments[0]; + + return !!firstArg && firstArg.type.toString() === 'Origin'; + } + + /** + * @description The [[FunctionMetadata]] + */ + get meta (): FunctionMetadata { + return this._meta; + } +} diff --git a/packages/types/src/primitive/Moment.spec.ts b/packages/types/src/primitive/Moment.spec.ts new file mode 100644 index 0000000000..0ee8d2ad9f --- /dev/null +++ b/packages/types/src/primitive/Moment.spec.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import { CodecTo } from '../types'; +import Moment from './Moment'; +import U64 from './U64'; + +describe('Moment', () => { + describe('decode', () => { + const testDecode = (type: string, input: Date | Moment | U64 | number, expected: string | number, toJSON: boolean = false) => + it(`can decode from ${type}`, () => { + expect(new Moment(input)[toJSON ? 'toJSON' : 'toISOString']()).toBe(expected); + }); + + testDecode('Date', new Date(1537968546280), '2018-09-26T13:29:06.280Z'); + testDecode('Moment', new Moment(1234), 1234, true); + testDecode('number', 1234, 1234, true); + testDecode('U64', new U64(69), 69, true); + }); + + describe('encode', () => { + const testEncode = (to: 'toBn' | 'toISOString' | 'toNumber' | CodecTo, expected: BN | number | string | Uint8Array) => + it(`can encode ${to}`, () => { + expect(new Moment(421)[to]()).toEqual(expected); + }); + + testEncode('toBn', new BN(421)); + testEncode('toJSON', 421); + testEncode('toISOString', '1970-01-01T00:07:01.000Z'); + + testEncode('toNumber', 421); + testEncode('toU8a', Uint8Array.from([165, 1, 0, 0, 0, 0, 0, 0])); + + it(`can encode toString`, () => { + expect( + new Moment(421) + .toString() + .startsWith('Thu Jan 01 1970') // The time depends on the timezone this test is run in + ).toBe(true); + }); + }); + + describe('utils', () => { + it('compares values', () => { + expect(new Moment(123).eq(123)).toBe(true); + }); + + it('compares values (non-match)', () => { + expect(new Moment(123).eq(456)).toBe(false); + }); + }); +}); diff --git a/packages/types/src/primitive/Moment.ts b/packages/types/src/primitive/Moment.ts new file mode 100644 index 0000000000..70936c66f6 --- /dev/null +++ b/packages/types/src/primitive/Moment.ts @@ -0,0 +1,127 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { bnToBn, bnToHex, bnToU8a, isString, isU8a, u8aToBn } from '@plugnet/util'; + +import { UIntBitLength } from '../codec/AbstractInt'; +import { AnyNumber, Codec } from '../types'; + +const BITLENGTH: UIntBitLength = 64; + +/** + * @name Moment + * @description + * A wrapper around seconds/timestamps. Internally the representation only has + * second precicion (aligning with Rust), so any numbers passed an/out are always + * per-second. For any encoding/decoding the 1000 multiplier would be applied to + * get it in line with JavaScript formats. It extends the base JS `Date` object + * and has all the methods available that are applicable to any `Date` + * @noInheritDoc + */ +export default class Moment extends Date implements Codec { + protected raw: Date; // FIXME Remove this once we convert all types out of Base + + constructor (value: Moment | Date | AnyNumber = 0) { + super( + Moment.decodeMoment(value) + ); + + this.raw = this; + } + + static decodeMoment (value: Moment | Date | AnyNumber): Date { + if (value instanceof Date) { + return value; + } else if (isU8a(value)) { + value = u8aToBn(value.subarray(0, BITLENGTH / 8), true); + } else if (isString(value)) { + value = new BN(value, 10, 'le'); + } + + return new Date( + bnToBn(value as BN).toNumber() * 1000 + ); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return BITLENGTH / 8; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.getTime() === 0; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return Moment.decodeMoment(other).getTime() === this.getTime(); + } + + /** + * @description Returns the number of bits in the value + */ + bitLength (): UIntBitLength { + return BITLENGTH; + } + + /** + * @description Returns the BN representation of the timestamp + */ + toBn (): BN { + return new BN(this.toNumber()); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return bnToHex(this.toBn(), BITLENGTH); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toNumber(); + } + + /** + * @description Returns the number representation for the timestamp + */ + toNumber (): number { + return Math.ceil(this.getTime() / 1000); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + // only included here since we do not inherit docs + return super.toString(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return bnToU8a(this.toNumber(), BITLENGTH, true); + } +} + +/** + * @name MomentOf + * @description + * The Substrate MomentOf representation as a [[Moment]]. + */ +export class MomentOf extends Moment { +} diff --git a/packages/types/src/primitive/Null.spec.ts b/packages/types/src/primitive/Null.spec.ts new file mode 100644 index 0000000000..5310bbabb9 --- /dev/null +++ b/packages/types/src/primitive/Null.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Null from './Null'; + +describe('Null', () => { + it('compares against null', () => { + expect(new Null().eq(null)).toBe(true); + }); + + it('compares against Null', () => { + expect(new Null().eq(new Null())).toBe(true); + }); + + it('compares against other (failed)', () => { + expect(new Null().eq()).toBe(false); + }); +}); diff --git a/packages/types/src/primitive/Null.ts b/packages/types/src/primitive/Null.ts new file mode 100644 index 0000000000..b85901ee13 --- /dev/null +++ b/packages/types/src/primitive/Null.ts @@ -0,0 +1,63 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec } from '../types'; +import { isNull } from '@plugnet/util'; + +/** + * @name Null + * @description + * Implements a type that does not contain anything (apart from `null`) + */ +export default class Null implements Codec { + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return 0; + } + + /** + * @description Checks if the value is an empty value (always true) + */ + get isEmpty (): boolean { + return true; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return other instanceof Null || isNull(other); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return '0x'; + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return null; + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return ''; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return new Uint8Array(); + } +} diff --git a/packages/types/src/primitive/StorageData.ts b/packages/types/src/primitive/StorageData.ts new file mode 100644 index 0000000000..eec6001242 --- /dev/null +++ b/packages/types/src/primitive/StorageData.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Bytes from './Bytes'; + +/** + * @name StorageData + * @description + * Data retrieved via Storage queries and data for KeyValue pairs + */ +export default class StorageData extends Bytes { +} diff --git a/packages/types/src/primitive/StorageKey.spec.ts b/packages/types/src/primitive/StorageKey.spec.ts new file mode 100644 index 0000000000..6624d5d87b --- /dev/null +++ b/packages/types/src/primitive/StorageKey.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a } from '@plugnet/util'; +import storage from '@plugnet/storage/static'; + +import StorageKey from './StorageKey'; + +describe('StorageKey', () => { + it(`should correctly get Alice's freeBalance storage key (hex)`, () => { + expect( + new StorageKey( + storage + .balances + .freeBalance('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY') + ) + .toHex() + ).toBe('0xc99f5446efa57788f39ab529311f4550'); // FIXME OK this should be length-prefixed in reality + }); + + it(`should correctly get Alice's freeBalance storage key (u8a)`, () => { + expect( + new StorageKey( + storage + .balances + .freeBalance('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY') + ) + .toU8a() + ).toEqual( + // length prefix attached + hexToU8a('0x40c99f5446efa57788f39ab529311f4550') + ); + }); +}); diff --git a/packages/types/src/primitive/StorageKey.ts b/packages/types/src/primitive/StorageKey.ts new file mode 100644 index 0000000000..c84245633e --- /dev/null +++ b/packages/types/src/primitive/StorageKey.ts @@ -0,0 +1,93 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import { isFunction } from '@plugnet/util'; + +import Bytes from './Bytes'; +import { StorageFunctionMetadata } from '../Metadata/v0/Modules'; + +export interface StorageFunction { + (arg?: any): Uint8Array; + meta: StorageFunctionMetadata; + method: string; + section: string; + toJSON: () => any; + headKey?: Uint8Array; +} + +/** + * @name StorageKey + * @description + * A representation of a storage key (typically hashed) in the system. It can be + * constructed by passing in a raw key or a StorageFunction with (optional) arguments. + */ +export default class StorageKey extends Bytes { + private _meta: StorageFunctionMetadata | null; + private _outputType: string | null; + + constructor (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]) { + super(StorageKey.decodeStorageKey(value)); + + this._meta = StorageKey.getMeta(value as StorageKey); + this._outputType = StorageKey.getType(value as StorageKey); + } + + static decodeStorageKey (value: AnyU8a | StorageKey | StorageFunction | [StorageFunction, any]): Uint8Array { + if (isFunction(value)) { + return value(); + } else if (Array.isArray(value)) { + const [fn, ...arg] = value; + + if (isFunction(fn)) { + return fn(...arg); + } + } + + return value as Uint8Array; + } + + static getMeta (value: StorageKey | StorageFunction | [StorageFunction, any]): StorageFunctionMetadata | null { + if (value instanceof StorageKey) { + return value.meta; + } else if (isFunction(value)) { + return value.meta; + } else if (Array.isArray(value)) { + const [fn] = value; + + return fn.meta; + } + + return null; + } + + static getType (value: StorageKey | StorageFunction | [StorageFunction, any]): string | null { + if (value instanceof StorageKey) { + return value.outputType; + } else if (isFunction(value)) { + return value.meta.type.toString(); + } else if (Array.isArray(value)) { + const [fn] = value; + + return fn.meta.type.toString(); + } + + return null; + } + + /** + * @description The metadata or `null` when not available + */ + get meta (): StorageFunctionMetadata | null { + return this._meta; + } + + /** + * @description The output type, `null` when not available + */ + get outputType (): string | null { + return this._outputType; + } +} diff --git a/packages/types/src/primitive/Text.spec.ts b/packages/types/src/primitive/Text.spec.ts new file mode 100644 index 0000000000..14b0e5512d --- /dev/null +++ b/packages/types/src/primitive/Text.spec.ts @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { CodecTo } from '../types'; +import U8a from '../codec/U8a'; +import Text from './Text'; + +describe('Text', () => { + describe('decode', () => { + const testDecode = (type: string, input: string | Uint8Array | { toString: () => string }, expected: string) => + it(`can decode from ${type}`, () => { + expect(new Text(input).toString()).toBe(expected); + }); + + testDecode('string', 'foo', 'foo'); + testDecode('Text', new Text('foo'), 'foo'); + testDecode('Uint8Array', Uint8Array.from([12, 102, 111, 111]), 'foo'); + testDecode('U8a', new U8a(Uint8Array.from([12, 102, 111, 111])), 'foo'); + testDecode('object with `toString()`', { toString (): string { return 'foo'; } }, 'foo'); + }); + + describe('encode', () => { + const testEncode = (to: CodecTo, expected: string | Uint8Array) => + it(`can encode ${to}`, () => { + expect(new Text('foo')[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x0c666f6f'); + testEncode('toString', 'foo'); + testEncode('toU8a', Uint8Array.from([12, 102, 111, 111])); + }); + + describe('utils', () => { + it('compares actual string values', () => { + expect(new Text('123').eq('123')).toBe(true); + }); + + it('compares actual String values', () => { + expect(new Text('XYX').eq(new String('XYX'))).toBe(true); + }); + + it('compares actual non-string values (fails)', () => { + expect(new Text('123').eq(123)).toBe(false); + }); + + it('calulates the length & encoded length correctly for ASCII', () => { + const test = new Text('abcde'); + + expect(test.encodedLength).toEqual(6); + expect(test).toHaveLength(5); + }); + + it('calulates the length & encoded length correctly for non-ASCII', () => { + const test = new Text('中文'); + + expect(test.encodedLength).toEqual(7); + expect(test).toHaveLength(2); + }); + }); +}); diff --git a/packages/types/src/primitive/Text.ts b/packages/types/src/primitive/Text.ts new file mode 100644 index 0000000000..d11a8ae55b --- /dev/null +++ b/packages/types/src/primitive/Text.ts @@ -0,0 +1,106 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isString, stringToU8a, u8aToString, u8aToHex } from '@plugnet/util'; + +import { AnyU8a, Codec } from '../types'; +import Compact from '../codec/Compact'; + +/** + * @name Text + * @description + * This is a string wrapper, along with the length. It is used both for strings as well + * as items such as documentation. It simply extends the standard JS `String` built-in + * object, inheriting all methods exposed from `String`. + * @noInheritDoc + */ +// TODO +// - Strings should probably be trimmed (docs do come through with extra padding) +// - Potentially we want a "TypeString" extension to this. Basically something that +// wraps the `Balance`, `T::AccountId`, etc. The reasoning - with a "TypeString" +// we can nicely strip types down like "T::AcountId" -> "AccountId" +export default class Text extends String implements Codec { + constructor (value: Text | string | AnyU8a | { toString: () => string } = '') { + super( + Text.decodeText(value) + ); + } + + private static decodeText (value: Text | string | AnyU8a | { toString: () => string }): string { + if (isString(value)) { + return value.toString(); + } else if (value instanceof Uint8Array) { + const [offset, length] = Compact.decodeU8a(value); + + return u8aToString(value.subarray(offset, offset + length.toNumber())); + } + + return `${value}`; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.toU8a().length; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.length === 0; + } + + /** + * @description The length of the value + */ + get length (): number { + // only included here since we ignore inherited docs + return super.length; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return isString(other) + ? this.toString() === other.toString() + : false; + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toString(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + // only included here since we do not inherit docs + return super.toString(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + const encoded = stringToU8a(this.toString()); + + return isBare + ? encoded + : Compact.addLengthPrefix(encoded); + } +} diff --git a/packages/types/src/primitive/Type.spec.ts b/packages/types/src/primitive/Type.spec.ts new file mode 100644 index 0000000000..dc481a8101 --- /dev/null +++ b/packages/types/src/primitive/Type.spec.ts @@ -0,0 +1,94 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Text from './Text'; +import Type from './Type'; + +describe('Type', () => { + it('fails to cleanup invalid boxes', () => { + expect( + () => new Type('Box { + expect( + new Type('(AccountId)').toString() + ).toEqual('AccountId'); + }); + + it('does not touch tuples with multiple values', () => { + expect( + new Type('(AccountId, Balance)').toString() + ).toEqual('(AccountId,Balance)'); + }); + + it('handles nested types', () => { + expect( + new Type('Box>').toString() + ).toEqual('Vec'); + }); + + it('handles nested types (embedded)', () => { + expect( + new Type('(u32, Box>)').toString() + ).toEqual('(u32,Vec)'); + }); + + it('handles aliasses, multiples per line', () => { + expect( + new Type('(Vec, AccountId, Vec)').toString() + ).toEqual('(Bytes,AccountId,Bytes)'); + }); + + it('removes whitespaces', () => { + expect( + new Type('T :: AccountId').toString() + ).toEqual('AccountId'); + }); + + it('changes PairOf -> (T, T)', () => { + expect( + new Type('PairOf').toString() + ).toEqual('(Balance,Balance)'); + }); + + it('changes PairOf (embedded) -> (T, T)', () => { + expect( + new Type('(Vec, PairOf, Vec)').toString() + ).toEqual('(Bytes,(Balance,Balance),Vec)'); + }); + + it('changes () -> Null', () => { + expect( + new Type('()').toString() + ).toEqual('Null'); + }); + + it('does not allow toU8a', () => { + expect( + () => new Type().toU8a() + ).toThrow(/unimplemented/); + }); + + it('has a length for the type', () => { + expect( + new Type( + new Text(' Box ') + ).toString() + ).toEqual('Proposal'); // eslint-disable-line + }); + + it('unwraps compact', () => { + expect( + new Type('::Type').toString() + ).toEqual('Compact'); + }); + + it('handles InherentOfflineReport', () => { + expect( + new Type('::Inherent').toString() + ).toEqual('InherentOfflineReport'); + }); +}); diff --git a/packages/types/src/primitive/Type.ts b/packages/types/src/primitive/Type.ts new file mode 100644 index 0000000000..a28ce0d5ab --- /dev/null +++ b/packages/types/src/primitive/Type.ts @@ -0,0 +1,232 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Text from './Text'; + +type Mapper = (value: string) => string; + +const ALLOWED_BOXES = ['Compact', 'Option', 'Vec']; + +/** + * @name Type + * @description + * This is a extended version of String, specifically to handle types. Here we rely fully + * on what string provides us, however we also adjust the types received from the runtime, + * i.e. we remove the `T::` prefixes found in some types for consistency accross implementation. + */ +export default class Type extends Text { + private _originalLength: number; + + constructor (value: Text | Uint8Array | string = '') { + // First decode it with Text + const textValue = new Text(value); + + // Then cleanup the textValue to get the @polkadot/types type, and pass the + // sanitized value to constructor + super( + Type.decodeType( + textValue.toString() + ) + ); + + this._originalLength = textValue.encodedLength; + } + + private static decodeType (value: string): string { + const mappings: Array = [ + // alias ::Inherent -> InherentOfflineReport + Type._alias('::Inherent', 'InherentOfflineReport'), + // + Type._cleanupCompact(), + // Remove all the trait prefixes + Type._removeTraits(), + // remove PairOf -> (T, T) + Type._removePairOf(), + // remove boxing, `Box` -> `Proposal` + Type._removeWrap('Box'), + // remove generics, `MisbehaviorReport` -> `MisbehaviorReport` + Type._removeGenerics(), + // alias String -> Text (compat with jsonrpc methods) + Type._alias('String', 'Text'), + // alias () -> Null + Type._alias('\\\(\\\)', 'Null'), + // alias Vec -> Bytes + Type._alias('Compact', 'IndexCompact'), + // alias Vec -> Bytes + Type._alias('Vec', 'Bytes'), + // alias RawAddress -> Address + Type._alias('RawAddress', 'Address'), + // alias Lookup::Source to Address (_could_ be AccountId on certain chains) + Type._alias('Lookup::Source', 'Address'), + // alias Lookup::Target to AccountId (always the case) + Type._alias('Lookup::Target', 'AccountId'), + // flattens tuples with one value, `(AccountId)` -> `AccountId` + Type._flattenSingleTuple() + ]; + + return mappings.reduce((result, fn) => { + return fn(result); + }, value).trim(); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + // NOTE Length is used in the decoding calculations, so return the original (pre-cleanup) + // length of the data. Since toU8a is disabled, this does not affect encoding, but rather + // only the decoding leg, allowing the decoders to work with original pointers + return this._originalLength; + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + // Note Since we are mangling what we get in beyond recognition, we really should + // not allow the re-encoding. Additionally, this is probably more of a decoder-only + // helper, so treat it as such. + throw new Error('Type::toU8a: unimplemented'); + } + + // given a starting index, find the closing > + private static _findClosing (value: string, start: number): number { + let depth = 0; + + for (let index = start; index < value.length; index++) { + if (value[index] === '>') { + if (!depth) { + return index; + } + + depth--; + } else if (value[index] === '<') { + depth++; + } + } + + throw new Error(`Unable to find closing matching <> on '${value}' (start ${start})`); + } + + private static _alias (src: string, dest: string): Mapper { + return (value: string): string => { + return value.replace( + new RegExp(src, 'g'), dest + ); + }; + } + + private static _cleanupCompact (): Mapper { + return (value: string): string => { + for (let index = 0; index < value.length; index++) { + if (value[index] !== '<') { + continue; + } + + const end = Type._findClosing(value, index + 1) - 14; + + if (value.substr(end, 14) === ' as HasCompact') { + value = `Compact<${value.substr(index + 1, end - index - 1)}>`; + } + } + + return value; + }; + } + + private static _flattenSingleTuple (): Mapper { + return (value: string): string => { + return value.replace(/\(([^,]*)\)/, '$1'); + }; + } + + private static _removeGenerics (): Mapper { + return (value: string): string => { + for (let index = 0; index < value.length; index++) { + if (value[index] === '<') { + // check against the allowed wrappers, be it Vec<..>, Option<...> ... + const box = ALLOWED_BOXES.find((box) => { + const start = index - box.length; + + return start >= 0 && value.substr(start, box.length) === box; + }); + + // we have not found anything, unwrap generic innards + if (!box) { + const end = Type._findClosing(value, index + 1); + + value = `${value.substr(0, index)}${value.substr(end + 1)}`; + } + } + } + + return value; + }; + } + + // remove the PairOf wrappers + private static _removePairOf (): Mapper { + return (value: string): string => { + for (let index = 0; index < value.length; index++) { + if (value.substr(index, 7) === 'PairOf<') { + const start = index + 7; + const end = Type._findClosing(value, start); + const type = value.substr(start, end - start); + + value = `${value.substr(0, index)}(${type},${type})${value.substr(end + 1)}`; + } + } + + return value; + }; + } + + // remove the type traits + private static _removeTraits (): Mapper { + return (value: string): string => { + return value + // remove all whitespaces + .replace(/\s/g, '') + // anything `T::` to end up as `` + .replace(/T::/g, '') + // anything `Self::` to end up as `` + .replace(/Self::/g, '') + // `system::` with `` - basically we find `` + .replace(/system::/g, '') + // replace `::` (whitespaces were removed above) + .replace(/::/g, '') + // replace `::` (whitespaces were removed above) + .replace(/::/g, '') + // replace + .replace(//g, 'Lookup') + // replace `<...>::Type` + .replace(/::Type/g, '') + // replace `wasm::*` eg. `wasm::PrefabWasmModule` + .replace(/wasm::/g, ''); + }; + } + + // remove wrapping values, i.e. Box -> Proposal + private static _removeWrap (_check: string): Mapper { + const check = `${_check}<`; + + return (value: string): string => { + let index = 0; + + while (index !== -1) { + index = value.indexOf(check); + + if (index !== -1) { + const start = index + check.length; + const end = Type._findClosing(value, start); + + value = `${value.substr(0, index)}${value.substr(start, end - start)}${value.substr(end + 1)}`; + } + } + + return value; + }; + } +} diff --git a/packages/types/src/primitive/U128.ts b/packages/types/src/primitive/U128.ts new file mode 100644 index 0000000000..98e88f2bc4 --- /dev/null +++ b/packages/types/src/primitive/U128.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U128 + * @description + * An 128-bit unsigned integer + */ +export default class U128 extends UInt { + constructor (value?: AnyNumber) { + super(value, 128); + } +} diff --git a/packages/types/src/primitive/U16.ts b/packages/types/src/primitive/U16.ts new file mode 100644 index 0000000000..cf462340d7 --- /dev/null +++ b/packages/types/src/primitive/U16.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U16 + * @description + * An 16-bit unsigned integer + */ +export default class U16 extends UInt { + constructor (value?: AnyNumber) { + super(value, 16); + } +} diff --git a/packages/types/src/primitive/U256.ts b/packages/types/src/primitive/U256.ts new file mode 100644 index 0000000000..14e6890f84 --- /dev/null +++ b/packages/types/src/primitive/U256.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U256 + * @description + * An 256-bit unsigned integer + */ +export default class U256 extends UInt { + constructor (value?: AnyNumber) { + super(value, 256); + } +} diff --git a/packages/types/src/primitive/U32.ts b/packages/types/src/primitive/U32.ts new file mode 100644 index 0000000000..b7527d87f3 --- /dev/null +++ b/packages/types/src/primitive/U32.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U32 + * @description + * An 32-bit unsigned integer + */ +export default class U32 extends UInt { + constructor (value?: AnyNumber) { + super(value, 32); + } +} diff --git a/packages/types/src/primitive/U64.ts b/packages/types/src/primitive/U64.ts new file mode 100644 index 0000000000..3860af630f --- /dev/null +++ b/packages/types/src/primitive/U64.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U64 + * @description + * An 64-bit unsigned integer + */ +export default class U64 extends UInt { + constructor (value?: AnyNumber) { + super(value, 64); + } +} diff --git a/packages/types/src/primitive/U8.ts b/packages/types/src/primitive/U8.ts new file mode 100644 index 0000000000..02b9efaaf0 --- /dev/null +++ b/packages/types/src/primitive/U8.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import UInt from '../codec/UInt'; + +/** + * @name U8 + * @description + * An 8-bit unsigned integer + */ +export default class U8 extends UInt { + constructor (value?: AnyNumber) { + super(value, 8); + } +} diff --git a/packages/types/src/primitive/USize.ts b/packages/types/src/primitive/USize.ts new file mode 100644 index 0000000000..3e356dbc9f --- /dev/null +++ b/packages/types/src/primitive/USize.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U64 from './U64'; + +/** + * @name USize + * @description + * A System default unsigned number, typically used in RPC to report non-consensus + * data. It is a wrapper for [[U64]] as a 64-binary default + */ +export default class USize extends U64 { +} diff --git a/packages/types/src/primitive/index.ts b/packages/types/src/primitive/index.ts new file mode 100644 index 0000000000..92a825f923 --- /dev/null +++ b/packages/types/src/primitive/index.ts @@ -0,0 +1,38 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE When adding any types here, we need to update the coumentation links as +// well - /docs/SUMMARY.md as well as ../README.md + +/** + * @summary Type definitions that are used in the system + */ +// NOTE We are exporting Bool as bool to match with Rust (however also using Bool internally), +// so with the renamed versions, epose both cases, allowing createType to work +export { default as Bool, default as bool } from './Bool'; +export { default as Bytes } from './Bytes'; +export { default as Data } from './Data'; +export { default as H160 } from './H160'; +export { default as H256 } from './H256'; +export { default as H512 } from './H512'; +export { default as I8, default as i8 } from './I8'; +export { default as I16, default as i16 } from './I16'; +export { default as I32, default as i32 } from './I32'; +export { default as I64, default as i64 } from './I64'; +export { default as I128, default as i128 } from './I128'; +export { default as I256, default as i256 } from './I256'; +export { default as Method } from './Method'; +export { default as Moment, MomentOf } from './Moment'; +export { default as Null } from './Null'; +export { default as StorageData } from './StorageData'; +export { default as StorageKey } from './StorageKey'; +export { default as Text } from './Text'; +export { default as Type } from './Type'; +export { default as U8, default as u8 } from './U8'; +export { default as U16, default as u16 } from './U16'; +export { default as U32, default as u32 } from './U32'; +export { default as U64, default as u64 } from './U64'; +export { default as U128, default as u128 } from './U128'; +export { default as U256, default as u256 } from './U256'; +export { default as USize, default as usize } from './USize'; diff --git a/packages/types/src/rpc/ChainProperties.spec.ts b/packages/types/src/rpc/ChainProperties.spec.ts new file mode 100644 index 0000000000..71670362f3 --- /dev/null +++ b/packages/types/src/rpc/ChainProperties.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import ChainProperties from './ChainProperties'; + +describe('ChainProperties', () => { + it('decodes from a null value', () => { + expect( + [...new ChainProperties(null).entries()] + ).toEqual([]); + }); + + it('decodes from an actual object', () => { + expect( + [...new ChainProperties({ + decimals: 15, + tokenSymbol: 'BBQ' + }).entries()] + ).toEqual([['decimals', 15], ['tokenSymbol', 'BBQ']]); + }); + + it('checks equality', () => { + expect( + new ChainProperties({ + decimals: 15, + tokenSymbol: 'BBQ' + }).eq([['tokenSymbol', 'BBQ'], ['decimals', 15]]) + ).toBe(true); + }); +}); diff --git a/packages/types/src/rpc/ChainProperties.ts b/packages/types/src/rpc/ChainProperties.ts new file mode 100644 index 0000000000..910d500c5b --- /dev/null +++ b/packages/types/src/rpc/ChainProperties.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Json from './Json'; + +/** + * @name ChainProperties + * @description + * Wraps the properties retrieved from the chain via the `system.properties` RPC call. + */ +export default class ChainProperties extends Json { + /** + * @description The token decimals, if defined (de-facto standard only) + */ + get tokenDecimals (): number | undefined { + return this.get('tokenDecimals'); + } + + /** + * @description The token system, if defined (de-facto standard only) + */ + get tokenSymbol (): string | undefined { + return this.get('tokenSymbol'); + } +} diff --git a/packages/types/src/rpc/ExtrinsicStatus.spec.ts b/packages/types/src/rpc/ExtrinsicStatus.spec.ts new file mode 100644 index 0000000000..b30ac7d4d8 --- /dev/null +++ b/packages/types/src/rpc/ExtrinsicStatus.spec.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import rpc from '../json/ExtrinsicStatus.001.json'; +import ExtrinsicStatus from './ExtrinsicStatus'; + +describe('ExtrinsicStatus', () => { + const status = new ExtrinsicStatus(rpc.params.result); + + it('has the correct type', () => { + expect( + status.type + ).toEqual('Finalized'); + }); + + it('has the correct hash', () => { + expect( + status.value.toString() + ).toEqual('0xc465b92a72b1d20918d64cd4effa70c2bb58b53a3f8c24c3ac8fd8f465f059b4'); + }); +}); diff --git a/packages/types/src/rpc/ExtrinsicStatus.ts b/packages/types/src/rpc/ExtrinsicStatus.ts new file mode 100644 index 0000000000..83dd6531c0 --- /dev/null +++ b/packages/types/src/rpc/ExtrinsicStatus.ts @@ -0,0 +1,184 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../codec/EnumType'; +import Vector from '../codec/Vector'; +import Null from '../primitive/Null'; +import Text from '../primitive/Text'; +import Hash from '../type/Hash'; + +/** + * @name Broadcast + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] has been boradcast to peers + */ +export class Broadcast extends Vector.with(Text) { +} + +/** + * @name Dropped + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] has been dropped + */ +export class Dropped extends Null { +} + +/** + * @name Finalized + * @description + * An [[ExtrinsicStatus] indicating that the [[Extrinsic]]] has been finalized and included + */ +export class Finalized extends Hash { +} + +/** + * @name Future + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] has been added to the future queue + */ +export class Future extends Null { +} + +/** + * @name Ready + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] has been added to the ready queue + */ +export class Ready extends Null { +} + +/** + * @name Invalid + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] is invalid + */ +export class Invalid extends Null { +} + +/** + * @name Usurped + * @description + * An [[ExtrinsicStatus]] indicating that the [[Extrinsic]] has been usurped + */ +export class Usurped extends Hash { +} + +/** + * @name ExtrinsicStatus + * @description + * An [[EnumType]] that indicates the status of the [[Extrinsic]] as been submitted + */ +export default class ExtrinsicStatus extends EnumType { + constructor (value: any, index?: number) { + super({ + Future, + Ready, + Finalized, + Usurped, + Broadcast, + Dropped, + Invalid + }, value, index, { + 'Finalised': 'Finalized' // old UK spelling to US spelling + }); + } + + /** + * @description Returns the item as a [[Broadcast]] + */ + get asBroadcast (): Broadcast { + return this.value as Broadcast; + } + + /** + * @description Returns the item as a [[Dropped]] + */ + get asDropped (): Dropped { + return this.value as Dropped; + } + + /** + * @description Returns the item as a [[Finalized]] + */ + get asFinalized (): Finalized { + return this.value as Finalized; + } + + /** + * @description Returns the item as a [[Future]] + */ + get asFuture (): Future { + return this.value as Future; + } + + /** + * @description Returns the item as a [[Invalid]] + */ + get asInvalid (): Invalid { + return this.value as Invalid; + } + + /** + * @description Returns the item as a [[Ready]] + */ + get asReady (): Ready { + return this.value as Ready; + } + + /** + * @description Returns the item as a [[Usurped]] + */ + get asUsurped (): Usurped { + return this.value as Usurped; + } + + /** + * @description Returns true if the status is boadcast + */ + get isBroadcast (): boolean { + return this.type === 'Broadcast'; + } + + /** + * @description Returns true if the status is dropped + */ + get isDropped (): boolean { + return this.type === 'Dropped'; + } + + /** + * @description Returns true if the status is finalized + */ + get isFinalized (): boolean { + return this.type === 'Finalized'; + } + + /** + * @description Returns true if the status is future + */ + get isFuture (): boolean { + return this.type === 'Future'; + } + + /** + * @description Returns true if the status is invalid + */ + get isInvalid (): boolean { + return this.type === 'Invalid'; + } + + /** + * @description Returns true if the status is eady + */ + get isReady (): boolean { + return this.type === 'Ready'; + } + + /** + * @description Returns true if the status is usurped + */ + get isUsurped (): boolean { + return this.type === 'Usurped'; + } +} diff --git a/packages/types/src/rpc/Health.ts b/packages/types/src/rpc/Health.ts new file mode 100644 index 0000000000..39cfaa3911 --- /dev/null +++ b/packages/types/src/rpc/Health.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import Bool from '../primitive/Bool'; +import USize from '../primitive/USize'; + +const JSON_MAP = new Map([ + ['isSyncing', 'is_syncing'], + ['shouldHavePeers', 'should_have_peers'] +]); + +/** + * @name Health + * @description + * A system health indicator, reported back over RPC + */ +export default class Health extends Struct { + constructor (value?: any) { + super({ + peers: USize, + isSyncing: Bool, + shouldHavePeers: Bool + }, value, JSON_MAP); + } + + /** + * @description The sync status + */ + get isSyncing (): Bool { + return this.get('isSyncing') as Bool; + } + + /** + * @description The numbers of peers as [[U32]] + */ + get peers (): USize { + return this.get('peers') as USize; + } + + /** + * @description Should this node have peers (not active on --dev) + */ + get shouldHavePeers (): Bool { + return this.get('shouldHavePeers') as Bool; + } +} diff --git a/packages/types/src/rpc/Json.ts b/packages/types/src/rpc/Json.ts new file mode 100644 index 0000000000..41f9f4af20 --- /dev/null +++ b/packages/types/src/rpc/Json.ts @@ -0,0 +1,77 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec } from '../types'; +import { compareMap } from '../codec/utils'; + +/** + * @name Json + * @description + * Wraps the a JSON structure retrieve via RPC. It extends the standard JS Map with. While it + * implements a Codec, it is limited in that it can only be used with input objects via RPC, + * i.e. no hex decoding. Unlike a struct, this waps a JSON object with unknown keys + * @noInheritDoc + */ +export default class Json extends Map implements Codec { + constructor (value?: { [index: string]: any } | null) { + super(Json.decodeJson(value)); + } + + private static decodeJson (value?: { [index: string]: any } | null): Array<[string, any]> { + return Object.entries(value || {}); + } + + /** + * @description Always 0, never encodes as a Uint8Array + */ + get encodedLength (): number { + return 0; + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return [...this.keys()].length === 0; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return compareMap(this, other); + } + + /** + * @description Unimplemented, will throw + */ + toHex (): string { + throw new Error('Unimplemented'); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return [...this.entries()].reduce((json, [key, value]) => { + json[key] = value; + + return json; + }, {} as any); + } + + /** + * @description Returns the string representation of the value + */ + toString () { + return JSON.stringify(this.toJSON()); + } + + /** + * @description Unimplemented, will throw + */ + toU8a (isBare?: boolean): Uint8Array { + throw new Error('Unimplemented'); + } +} diff --git a/packages/types/src/rpc/NetworkState.ts b/packages/types/src/rpc/NetworkState.ts new file mode 100644 index 0000000000..10ab8c1332 --- /dev/null +++ b/packages/types/src/rpc/NetworkState.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Json from './Json'; + +/** + * @name NetworkState + * @description + * Wraps the properties retrieved from the chain via the `system.network_state` RPC call. + */ +export default class NetworkState extends Json { +} diff --git a/packages/types/src/rpc/PeerInfo.ts b/packages/types/src/rpc/PeerInfo.ts new file mode 100644 index 0000000000..3d86d0a9e4 --- /dev/null +++ b/packages/types/src/rpc/PeerInfo.ts @@ -0,0 +1,68 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; +import BlockNumber from '../type/BlockNumber'; +import Hash from '../type/Hash'; + +const JSON_MAP = new Map([ + ['bestHash', 'best_hash'], + ['bestNumber', 'best_number'], + ['protocolVersion', 'protocol_version'], + ['peerId', 'peer_id'] +]); + +/** + * @name PeerInfo + * @description + * A system peer info indicator, reported back over RPC + */ +export default class PeerInfo extends Struct { + constructor (value?: any) { + super({ + peerId: Text, + roles: Text, + protocolVersion: U32, + bestHash: Hash, + bestNumber: BlockNumber + }, value, JSON_MAP); + } + + /** + * @description The best block hash for the peer + */ + get bestHash (): Hash { + return this.get('bestHash') as Hash; + } + + /** + * @description The best block hash for the peer + */ + get bestNumber (): BlockNumber { + return this.get('bestNumber') as BlockNumber; + } + + /** + * @description The p2p network id for the peer + */ + get peerId (): Text { + return this.get('peerId') as Text; + } + + /** + * @description The index of the peer in our list + */ + get protocolVersion (): U32 { + return this.get('protocolVersion') as U32; + } + + /** + * @description The roles of the peer on the network + */ + get roles (): Text { + return this.get('roles') as Text; + } +} diff --git a/packages/types/src/rpc/PendingExtrinsics.ts b/packages/types/src/rpc/PendingExtrinsics.ts new file mode 100644 index 0000000000..a6d02e046e --- /dev/null +++ b/packages/types/src/rpc/PendingExtrinsics.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Extrinsics from '../type/Extrinsics'; + +/** + * @name PendingExtrinsics + * @description + * A list of pending [[Extrinsics]] + */ +export default class PendingExtrinsics extends Extrinsics { +} diff --git a/packages/types/src/rpc/RuntimeVersion.spec.ts b/packages/types/src/rpc/RuntimeVersion.spec.ts new file mode 100644 index 0000000000..c9a4cc668b --- /dev/null +++ b/packages/types/src/rpc/RuntimeVersion.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import rpc from '../json/RuntimeVersion.001.json'; +import RuntimeVersion from './RuntimeVersion'; + +describe('RuntimeVersion', () => { + const version = new RuntimeVersion(rpc.result); + + it('has the correct authoring', () => { + expect(version.authoringVersion.toNumber()).toEqual(1); + }); + + it('has the apis', () => { + const api = version.apis[0]; + + expect(api.id.toHex()).toEqual('0x696e686572656e74'); + expect(api.version.toNumber()).toEqual(1); + }); + + it('has the correct implementation', () => { + expect(version.implName.toString()).toEqual('substrate-node'); + expect(version.implVersion.toNumber()).toEqual(0); + }); + + it('has the correct spec', () => { + expect(version.specName.toString()).toEqual('node'); + expect(version.specVersion.toNumber()).toEqual(1); + }); +}); diff --git a/packages/types/src/rpc/RuntimeVersion.ts b/packages/types/src/rpc/RuntimeVersion.ts new file mode 100644 index 0000000000..1051edf98c --- /dev/null +++ b/packages/types/src/rpc/RuntimeVersion.ts @@ -0,0 +1,131 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber, AnyU8a, RuntimeVersionInterface } from '../types'; + +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import U8aFixed from '../codec/U8aFixed'; +import Vector from '../codec/Vector'; +import Text from '../primitive/Text'; +import U32 from '../primitive/U32'; + +/** + * @name ApiId + * @description + * An identifier for the runtime API + */ +export class ApiId extends U8aFixed { + constructor (value?: AnyU8a) { + super(value, 64); + } +} + +type RuntimeVersionApiValue = { + id?: AnyU8a, + version?: AnyNumber +}; + +/** + * @name RuntimeVersionApi + * @description + * A [[Tuple]] that conatins the [[ApiId]] and [[U32]] version + */ +export class RuntimeVersionApi extends Tuple { + constructor (value?: RuntimeVersionApiValue | Uint8Array) { + super({ + ApiId, + U32 + }, value); + } + + /** + * @description The [[ApiId]] + */ + get id (): ApiId { + return this[0] as ApiId; + } + + /** + * @description The specific version as [[U32]] + */ + get version (): U32 { + return this[1] as U32; + } +} + +type RuntimeVersionValue = { + specName?: string, + implName?: string, + authoringVersion?: AnyNumber, + specVersion?: AnyNumber, + implVersion?: AnyNumber, + apis?: Array +}; + +/** + * @name RuntimeVersion + * @description + * A defintion of the runtime and the associated versions thereof + */ +export default class RuntimeVersion extends Struct implements RuntimeVersionInterface { + constructor (value?: RuntimeVersionValue | Uint8Array) { + super({ + specName: Text, + implName: Text, + authoringVersion: U32, + specVersion: U32, + implVersion: U32, + apis: Vector.with(RuntimeVersionApi) + }, value, new Map([ + ['authoringVersion', 'authoring_version'], + ['implName', 'impl_name'], + ['implVersion', 'impl_version'], + ['specName', 'spec_name'], + ['specVersion', 'spec_version'] + ])); + } + + /** + * @description The available APIs as [[RuntimeVersionApi]] + */ + get apis (): Vector { + return this.get('apis') as Vector; + } + + /** + * @description The authoring version as [[U32]] + */ + get authoringVersion (): U32 { + return this.get('authoringVersion') as U32; + } + + /** + * @description The implementation name + */ + get implName (): Text { + return this.get('implName') as Text; + } + + /** + * @description The implementation version + */ + get implVersion (): U32 { + return this.get('implVersion') as U32; + } + + /** + * @description The specification name + */ + get specName (): Text { + return this.get('specName') as Text; + } + + /** + * @description The specification version + */ + get specVersion (): U32 { + return this.get('specVersion') as U32; + } +} diff --git a/packages/types/src/rpc/SignedBlock.spec.ts b/packages/types/src/rpc/SignedBlock.spec.ts new file mode 100644 index 0000000000..9b61221483 --- /dev/null +++ b/packages/types/src/rpc/SignedBlock.spec.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsics from '@plugnet/extrinsics/static'; + +import Extrinsics from '../type/Extrinsics'; +import Method from '../primitive/Method'; +import rpc from '../json/SignedBlock.001.json'; +import events from '../json/SignedBlock.002.json'; +import SignedBlock from './SignedBlock'; + +describe.skip('SignedBlock', () => { + let block: SignedBlock; + + beforeEach(() => { + Method.injectMethods(extrinsics); + + block = new SignedBlock(rpc.result); + }); + + it('has the correct stateRoot', () => { + expect( + block.block.header.stateRoot.toString() + ).toEqual('0xbfe02f7c26e5dad1226eccb1f4ef5306312602f7e1edbb9be50e8b87d9c7840b'); + }); + + it('decodes a block with logs', () => { + const s = new SignedBlock(events.result); + + expect(s.block.header.digest.logs.length).toBe(1); + expect(s.block.header.digest.logs[0].type).toBe('AuthoritiesChange'); + expect(s.block.header.digest.logs[0].value.toJSON()).toEqual([ + '5EyrU8k6worwXWfW2LajRvS7XF6eta286tMpHeHUwMPpzQ9F', + '5Dpq7rU6KAZVdRtcegvpCoeJtZzFPzieRkRz4xajRAiMRkCf', + '5CCtQPAYtuDXt6H4WMrn5nFNk4gemkVRD3k1r1mA7YiZLPBq', + '5F2ABwFm3fUhWMZT7Zeu7UjCr1dndjJD7qBeHChJhiJC949V', + '5CA1n5DSpE46Q9Y7NaqD39T9X7S2dRtscsPvrv2dWuYhFtWr', + '5CrxmsTCLbPRzHnFPs8Q6cx8jzHweegMSQYEJBGXLjDZW4NR', + '5EQ4ZxYew2CXPkjGCBuPdTVg7Hp3bmBXiXBwn1iDmvj6CgaF' + ]); + }); + + describe('extrinsics', () => { + let extrinsics: Extrinsics; + + beforeEach(() => { + extrinsics = block.block.extrinsics; + }); + + it('has the correct data for the first', () => { + const x = extrinsics[0]; + + expect(x.callIndex).toEqual(new Uint8Array([2, 0])); + expect(x.method.args[0].toU8a()).toEqual(new Uint8Array([70, 41, 195, 91, 0, 0, 0, 0])); + }); + + it('has the correct data for the second', () => { + const x = extrinsics[1]; + + expect(x.callIndex).toEqual(new Uint8Array([1, 0])); + expect(x.method.args[0].toString()).toEqual('5CPaGq4KcmntaboAxg5nyqGXdyzaBV2hj6PvhNA3syigiRg8'); + expect(x.method.args[1].toString()).toEqual('100000000000000'); + }); + }); +}); diff --git a/packages/types/src/rpc/SignedBlock.ts b/packages/types/src/rpc/SignedBlock.ts new file mode 100644 index 0000000000..1182966ec3 --- /dev/null +++ b/packages/types/src/rpc/SignedBlock.ts @@ -0,0 +1,50 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import Struct from '../codec/Struct'; +import Block, { BlockValue } from '../type/Block'; +import Hash from '../type/Hash'; +import Justification from '../type/Justification'; + +type SignedBlockValue = { + block?: BlockValue, + justification?: AnyU8a +}; + +/** + * @name SignedBlock + * @description + * A [[Block]] that has been signed and contains a [[Justification]] + */ +export default class SignedBlock extends Struct { + constructor (value?: SignedBlockValue | Uint8Array) { + super({ + block: Block, + justification: Justification + }, value); + } + + /** + * @description The wrapped [[Block]] + */ + get block (): Block { + return this.get('block') as Block; + } + + /** + * @description Block/header [[Hash]] + */ + get hash (): Hash { + return this.block.hash; + } + + /** + * @description The wrapped [[Justification]] + */ + get justification (): Justification { + return this.get('justification') as Justification; + } +} diff --git a/packages/types/src/rpc/StorageChangeSet.spec.ts b/packages/types/src/rpc/StorageChangeSet.spec.ts new file mode 100644 index 0000000000..7f2ad3ddf2 --- /dev/null +++ b/packages/types/src/rpc/StorageChangeSet.spec.ts @@ -0,0 +1,51 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { createType } from '@plugnet/types'; +import json from '../json/StorageChangeSet.001.json'; +import StorageChangeSet from './StorageChangeSet'; + +describe('StorageChangeSet', () => { + describe('construction', () => { + const set = new StorageChangeSet({ + block: '0x1234', + changes: [ + ['0xab', '0xcd'] + ] + }); + + it('wraps blockHash', () => { + expect( + set.block.toHex() + ).toEqual('0x1234000000000000000000000000000000000000000000000000000000000000'); + }); + + it('wraps key/value', () => { + expect( + set.changes[0].value.toString() + ).toEqual('0xcd'); + }); + }); + + describe('json', () => { + const set = new StorageChangeSet(json.params.result); + + it('has the correct hash', () => { + expect( + set.block.toHex() + ).toEqual('0x2ad8077937e9a5ceb2e0d57c95b95a6a9edcedc4fb1f14e3bc13245e223a569d'); + }); + + it('has the changes', () => { + expect(set.changes).toHaveLength(1); + expect(set.changes[0].key.toHex()).toEqual('0x54bdbdb5e438d574dd4da05ee6131cee'); + }); + + it('converts result data to Bytes', () => { + const bytes = createType('Bytes', set.changes[0].value.unwrap()); + + expect(bytes).toHaveLength(4452); + }); + }); +}); diff --git a/packages/types/src/rpc/StorageChangeSet.ts b/packages/types/src/rpc/StorageChangeSet.ts new file mode 100644 index 0000000000..15758066b2 --- /dev/null +++ b/packages/types/src/rpc/StorageChangeSet.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import Struct from '../codec/Struct'; +import Vector from '../codec/Vector'; +import Hash from '../type/Hash'; +import { KeyValueOption, KeyValueOptionValue } from '../type/KeyValue'; + +type StorageChangeSetValue = { + block?: AnyU8a, + changes?: Array +}; + +/** + * @name StorageChangeSet + * @description + * A set of storage changes. It contains the [[Block]] hash and + * a list of the actual changes that took place as an array of + * [[KeyValueOption]] + */ +export default class StorageChangeSet extends Struct { + constructor (value?: StorageChangeSetValue | Uint8Array) { + super({ + block: Hash, + changes: Vector.with(KeyValueOption) + }, value); + } + + /** + * @description The applicable changes as [[KeyValueOption]] + */ + get changes (): Vector { + return this.get('changes') as Vector; + } + + /** + * @description The block [[Hash]] + */ + get block (): Hash { + return this.get('block') as Hash; + } +} diff --git a/packages/types/src/rpc/index.ts b/packages/types/src/rpc/index.ts new file mode 100644 index 0000000000..c791d1e880 --- /dev/null +++ b/packages/types/src/rpc/index.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE When adding any types here, we need to update the coumentation links as +// well - /docs/SUMMARY.md as well as ../README.md + +/** + * @summary Type definitions that are used in the rpc + */ +export { default as ChainProperties } from './ChainProperties'; +export { default as ExtrinsicStatus } from './ExtrinsicStatus'; +export { default as Health } from './Health'; +export { default as Json } from './Json'; +export { default as NetworkState } from './NetworkState'; +export { default as PeerInfo } from './PeerInfo'; +export { default as PendingExtrinsics } from './PendingExtrinsics'; +export { default as RuntimeVersion } from './RuntimeVersion'; +export { default as SignedBlock } from './SignedBlock'; +export { default as StorageChangeSet } from './StorageChangeSet'; diff --git a/packages/types/src/scripts/METHODS_STORAGE_SUBSTRATE.md b/packages/types/src/scripts/METHODS_STORAGE_SUBSTRATE.md new file mode 100644 index 0000000000..f0f6edc58b --- /dev/null +++ b/packages/types/src/scripts/METHODS_STORAGE_SUBSTRATE.md @@ -0,0 +1,26 @@ + +--- + +### substrate + +_These are keys that are always available to the runtime implementation_ + +▸ **authorityCount**(): `u32` +- **summary**: Number of authorities. + +▸ **authorityPrefix**(): `u32` +- **summary**: Prefix under which authorities are storied. + +▸ **changesTrieConfig**(): `u32` +- **summary**: Changes trie configuration is stored under this key. + +▸ **code**(): `Bytes` +- **summary**: Wasm code of the runtime. + +▸ **extrinsicIndex**(): `u32` +- **summary**: Current extrinsic index (u32) is stored under this key. + +▸ **heapPages**(): `u64` +- **summary**: Number of wasm linear memory pages required for execution of the runtime. + +--- diff --git a/packages/types/src/scripts/MetadataMd.ts b/packages/types/src/scripts/MetadataMd.ts new file mode 100644 index 0000000000..286f7002b0 --- /dev/null +++ b/packages/types/src/scripts/MetadataMd.ts @@ -0,0 +1,200 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import fs from 'fs'; +import { stringCamelCase, stringLowerFirst } from '@plugnet/util'; + +import interfaces from '../../../type-jsonrpc/src'; +import Method from '../primitive/Method'; +import Metadata from '../Metadata'; +import rpcdata from '../Metadata/static'; +import MetadataV0 from '../Metadata/v0'; + +const ANCHOR_TOP = ''; +const LINK_BACK_TO_TOP = ''; + +const DESC_EXTRINSICS = '\n\n_The following sections contain Extrinsics methods are part of the default Substrate runtime._\n'; +const DESC_EVENTS = '\n\nEvents are emitted for certain operations on the runtime. The following sections describe the events that are part of the default Substrate runtime.\n'; +const DESC_RPC = '\n\n_The following sections contain RPC methods that are Remote Calls available by default and allow you to interact with the actual node, query, and submit. The RPCs are provided by Substrate itself._'; +const DESC_STORAGE = '\n\n_The following sections contain Storage methods are part of the default Substrate runtime._\n'; + +function sectionLink (sectionName: string) { + return `- **[${stringCamelCase(sectionName)}](#${stringCamelCase(sectionName)})**\n\n`; +} + +function generateSectionHeader (md: string, sectionName: string) { + return `${md}\n___\n${LINK_BACK_TO_TOP}\n\n### ${sectionName}\n`; +} + +function addRpc () { + const renderHeading = `## ${ANCHOR_TOP}JSON-RPC${DESC_RPC}\n`; + const orderedSections = Object.keys(interfaces).sort(); + const renderAnchors = Object.keys(interfaces).sort().map((sectionName) => + sectionLink(sectionName) + ).join(''); + + return orderedSections.reduce((md, sectionName) => { + const section = interfaces[sectionName]; + const renderSection = generateSectionHeader(md, sectionName) + `\n_${section.description}_\n`; + const orderedMethods = Object.keys(section.methods).sort(); + + return orderedMethods.reduce((md, methodName) => { + const method = section.methods[methodName]; + const args = method.params.map(({ name, isOptional, type }) => { + return name + (isOptional ? '?' : '') + ': `' + type + '`'; + }).join(', '); + const type = '`' + method.type + '`'; + // const isSub = method.isSubscription; + const renderMethod = `${md}\n▸ **${methodName}**(${args})`; + const renderReturnType = `: ${type}`; + const renderSignature = `${renderMethod}${renderReturnType}`; + const renderSummary = `${method && method.description ? `\n- **summary**: ${method.description}\n` : `\n\n`}`; + + return `${renderSignature}${renderSummary}`; + }, renderSection); + }, renderHeading + renderAnchors); +} + +function sortExtrinsicsSectionMethods (a: any, b: any) { + // ignore upper and lowercase + const nameA = a.name.toString().toUpperCase(); + const nameB = b.name.toString().toUpperCase(); + + return nameA.localeCompare(nameB); +} + +function addEvents (metadata: MetadataV0) { + const renderHeading = `## ${ANCHOR_TOP}Events${DESC_EVENTS}`; + const orderedSections = metadata.events.map((i) => i).sort(); + let renderAnchors = ''; + const sections = orderedSections.reduce((md, meta) => { + if (!meta.events || !meta.events.length) { + return md; + } + + const sectionName = stringCamelCase(meta.name.toString()); + + renderAnchors += sectionLink(sectionName); + + const renderSection = generateSectionHeader(md, sectionName); + + const orderedMethods = meta.events.sort(sortExtrinsicsSectionMethods); + + return orderedMethods.reduce((md, func) => { + const methodName = func.name.toString(); + const args = func.arguments.map((type) => '`' + type + '`').join(', '); + const doc = func.documentation.reduce((md, doc) => `${md} ${doc}`, ''); + const renderSignature = `${md}\n▸ **${methodName}**(${args})`; + const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`; + + return renderSignature + renderSummary; + }, renderSection); + }, ''); + + return renderHeading + renderAnchors + sections; +} + +function addExtrinsics (metadata: MetadataV0) { + const renderHeading = `## ${ANCHOR_TOP}Extrinsics${DESC_EXTRINSICS}`; + const orderedSections = metadata.modules.map((i) => i).sort(); + let renderAnchors = ''; + const sections = orderedSections.reduce((md, meta) => { + if (!meta.module.call || !meta.module.call.functions.length) { + return md; + } + + const sectionName = stringCamelCase(meta.prefix.toString()); + + renderAnchors += sectionLink(sectionName); + + const renderSection = generateSectionHeader(md, sectionName); + const orderedMethods = meta.module.call.functions.sort(sortExtrinsicsSectionMethods); + + return orderedMethods.reduce((md, func) => { + const methodName = stringCamelCase(func.name.toString()); + const args = Method.filterOrigin(func).map(({ name, type }) => `${name}: ` + '`' + type + '`').join(', '); + const doc = func.documentation.reduce((md, doc) => `${md} ${doc}`, ''); + const renderSignature = `${md}\n▸ **${methodName}**(${args})`; + const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`; + + return renderSignature + renderSummary; + }, renderSection); + }, ''); + + return renderHeading + renderAnchors + sections; +} + +function addStorage (metadata: MetadataV0) { + const renderHeading = `## ${ANCHOR_TOP}Storage${DESC_STORAGE}`; + const orderedSections = metadata.modules.sort(); + let renderAnchors = ''; + const sections = orderedSections.reduce((md, moduleMetadata) => { + if (moduleMetadata.storage.isNone) { + return md; + } + + const sectionName = stringLowerFirst(moduleMetadata.storage.unwrap().prefix.toString()); + + renderAnchors += sectionLink(sectionName); + + const renderSection = generateSectionHeader(md, sectionName); + const orderedMethods = moduleMetadata.storage.unwrap().functions.sort(); + + return orderedMethods.reduce((md, func) => { + const methodName = stringLowerFirst(func.name.toString()); + const arg = func.type.isMap ? ('`' + func.type.asMap.key.toString() + '`') : ''; + const doc = func.documentation.reduce((md, doc) => `${md} ${doc}`, ''); + const type = func.modifier.isOptional + ? `Option<${func.type}>` + : func.type; + const renderSignature = `${md}\n▸ **${methodName}**(${arg}): ` + '`' + type + '`'; + const renderSummary = `${doc ? `\n- **summary**: ${doc}\n` : '\n'}`; + + return renderSignature + renderSummary; + }, renderSection); + }, ''); + + return renderHeading + renderAnchors + sectionLink('substrate') + sections; +} + +function writeFile (name: string, ...chunks: Array) { + const options = { flags: 'w', encoding: 'utf8' }; + const writeStream = fs.createWriteStream(name, options); + + writeStream.on('finish', () => { + console.log(`Completed writing ${name}`); + }); + + chunks.forEach((chunk) => + writeStream.write(chunk) + ); + + writeStream.end(); +} + +function writeToRpcMd () { + writeFile('docs/METHODS_RPC.md', addRpc()); +} + +function writeToStorageMd (metadata: MetadataV0) { + const options = { flags: 'r', encoding: 'utf8' }; + const data = fs.readFileSync('packages/types/src/scripts/METHODS_STORAGE_SUBSTRATE.md', options); + + writeFile('docs/METHODS_STORAGE.md', addStorage(metadata), data); +} + +function writeToExtrinsicsMd (metadata: MetadataV0) { + writeFile('docs/METHODS_EXTRINSICS.md', addExtrinsics(metadata)); +} + +function writeToEventsMd (metadata: MetadataV0) { + writeFile('docs/METHODS_EVENTS.md', addEvents(metadata)); +} + +const metadata = new Metadata(rpcdata).asV0; + +writeToRpcMd(); +writeToStorageMd(metadata); +writeToExtrinsicsMd(metadata); +writeToEventsMd(metadata); diff --git a/packages/types/src/scripts/MetadataMdWrapper.js b/packages/types/src/scripts/MetadataMdWrapper.js new file mode 100644 index 0000000000..9e97124d17 --- /dev/null +++ b/packages/types/src/scripts/MetadataMdWrapper.js @@ -0,0 +1,17 @@ +#!/usr/bin/env node +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +require('@babel/register')({ + extensions: ['.js', '.ts'], + plugins: [ + ['module-resolver', { + alias: { + '^@polkadot/types(.*)': './packages/types/src\\2' + } + }] + ] +}); + +require('./MetadataMd.ts'); diff --git a/packages/types/src/type/AccountId.spec.ts b/packages/types/src/type/AccountId.spec.ts new file mode 100644 index 0000000000..799783b1fa --- /dev/null +++ b/packages/types/src/type/AccountId.spec.ts @@ -0,0 +1,102 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { setAddressPrefix } from '@plugnet/keyring'; +import U8a from '../codec/U8a'; +import Vector from '../codec/Vector'; +import jsonVec from '../json/AccountIdVec.001.json'; +import AccountId from './AccountId'; +import StorageData from '../primitive/StorageData'; + +describe('AccountId', () => { + describe('defaults', () => { + const id = new AccountId(); + + it('has a 32-byte length', () => { + expect(id).toHaveLength(32); + }); + + it('is empty by default', () => { + expect(id.isEmpty).toBe(true); + }); + + it('equals the empty address', () => { + expect(id.eq('5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM')).toBe(true); + }); + }); + + describe('decoding', () => { + const testDecode = (type: string, input: Uint8Array | string | AccountId, expected: string) => + it(`can decode from ${type}`, () => { + const a = new AccountId(input); + expect(a.toString()).toBe(expected); + }); + + testDecode( + 'AccountId', + new AccountId('0x0102030405060708010203040506070801020304050607080102030405060708'), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode('hex', '0x0102030405060708010203040506070801020304050607080102030405060708', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + testDecode('string', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + testDecode( + 'U8a', + new U8a([ + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ]), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode( + 'Uint8Array', + Uint8Array.from([ + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ]), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + }); + + describe('encoding', () => { + const testEncode = (to: 'toHex' | 'toJSON' | 'toString' | 'toU8a', expected: Uint8Array | string, input: string = '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF') => + it(`can encode ${to}`, () => { + const a = new AccountId(input); + + expect(a[to]()).toEqual(expected); + }); + + testEncode('toHex', '0x0102030405060708010203040506070801020304050607080102030405060708'); + testEncode('toJSON', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + testEncode('toString', '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + testEncode('toString', '5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM', '0x00'); + testEncode('toU8a', Uint8Array.from([ + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ])); + + it('decodes to a non-empty value', () => { + expect(new AccountId('7qT1BvpawNbqb3BZaBTMFMMAKrpJKLPf1LmEHR1JyarWJdMX').isEmpty).toBe(false); + }); + }); + + describe('storage decoding', () => { + it('has the correct entries', () => { + setAddressPrefix(68); + + const data = new StorageData(jsonVec.params.result.changes[0][1]); + const list = new Vector(AccountId, data).map((accountId) => accountId.toString()); + + expect(list).toEqual([ + '7qVJujLF3EDbZt5WfQXWvueFedMS4Vfk2Hb4GyR8jwksTLup', + '7pHyqeYaJjJPgxQgCXoS2EZMhBhtpm6BLCqQ4jJZTQB2kMhw', + '7pYLWV6PTUmLTMQfHmmuBwBNLkhcKhRAnkM36CSJtjat9ACb', + '7qT1BvpawNbqb3BZaBTMFMMAKrpJKLPf1LmEHR1JyarWJdMX', + '7rADc9JW5EUGFPWLjPMipH4c3bJ2GyAUedmqQHiaGucWVrsT', + '7oK5KRH6jt4p8auipnru9ptqeuRwbLMHA2tgCViZzhmW4Lox', + '7ndAVsHvonnzTg4AvRhpraNCKj9g4CGQXKoLrgkTZ91Na6PE', + '7oL7VfXgLA8L3pJJwi11v3sBYc1b5R3tLrweHwzMNxgEpjxP' + ]); + }); + }); +}); diff --git a/packages/types/src/type/AccountId.ts b/packages/types/src/type/AccountId.ts new file mode 100644 index 0000000000..66fed70d70 --- /dev/null +++ b/packages/types/src/type/AccountId.ts @@ -0,0 +1,71 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyString, AnyU8a } from '../types'; + +import { decodeAddress, encodeAddress } from '@plugnet/keyring'; +import { hexToU8a, isHex, isString, isU8a, u8aToU8a } from '@plugnet/util'; + +import U8aFixed from '../codec/U8aFixed'; + +/** + * @name AccountId + * @description + * A wrapper around an AccountId/PublicKey representation. Since we are dealing with + * underlying PublicKeys (32 bytes in length), we extend from U8aFixed which is + * just a Uint8Array wrapper with a fixed length. + */ +export default class AccountId extends U8aFixed { + constructor (value: AnyU8a = new Uint8Array()) { + super( + AccountId.decodeAccountId(value), + 256 + ); + } + + static encode (value: Uint8Array): string { + return encodeAddress(value); + } + + private static decodeAccountId (value: AnyU8a | AnyString): Uint8Array { + if (isU8a(value) || Array.isArray(value)) { + return u8aToU8a(value); + } else if (isHex(value)) { + return hexToU8a(value.toString()); + } else if (isString(value)) { + return decodeAddress((value as String).toString()); + } + + return value; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return super.eq(AccountId.decodeAccountId(other)); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toString(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return AccountId.encode(this); + } +} + +/** + * @name AccountIdOf + * @description + * The Substrate AccountIdOf representation as a [[AccountId]]. + */ +export class AccountIdOf extends AccountId { +} diff --git a/packages/types/src/type/AccountIndex.spec.ts b/packages/types/src/type/AccountIndex.spec.ts new file mode 100644 index 0000000000..fc9f074499 --- /dev/null +++ b/packages/types/src/type/AccountIndex.spec.ts @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import AccountIndex from './AccountIndex'; + +describe('AccountIndex', () => { + it('creates a BN representation', () => { + expect( + new AccountIndex(new Uint8Array([17, 18, 19, 20])).toNumber() + ).toEqual(336794129); + }); + + it('creates a BN representation (from ss-58)', () => { + expect( + new AccountIndex('Mwz15xP2').toNumber() + ).toEqual(336794129); + }); + + it('constructs 2-byte from number', () => { + expect( + new AccountIndex(256 * 1).toString() + ).toEqual('25GUyv'); + }); + + it('constructs from number', () => { + expect( + new AccountIndex(new BN(336794129)).toString() + ).toEqual('Mwz15xP2'); + }); + + it('compares ss-58 values', () => { + expect(new AccountIndex(256 * 1).eq('25GUyv')).toBe(true); + }); + + it('compares numbers', () => { + expect(new AccountIndex('2jpAFn').eq(256 * 1)).toBe(true); + }); + + describe('calcLength', () => { + const testLength = (value: number, length: number) => + expect(AccountIndex.calcLength(value)).toEqual(length); + + it('returns 1 for <= 0xef', () => { + testLength(0xef, 1); + }); + + it('returns 2 for > 0xef', () => { + testLength(0xf0, 2); + }); + + it('returns 4 bytes for 32-bit inputs', () => { + testLength(0xffeeddcc, 4); + }); + + it('returns 8 bytes for larger inputs', () => { + testLength(0x122334455, 8); + }); + }); +}); diff --git a/packages/types/src/type/AccountIndex.ts b/packages/types/src/type/AccountIndex.ts new file mode 100644 index 0000000000..9e1007af62 --- /dev/null +++ b/packages/types/src/type/AccountIndex.ts @@ -0,0 +1,112 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { decodeAddress, encodeAddress } from '@plugnet/keyring'; +import { bnToBn, isBn, isNumber, isU8a, isHex } from '@plugnet/util'; + +import U32 from '../primitive/U32'; +import { AnyNumber } from '../types'; + +export const ENUMSET_SIZE = new BN(64); + +const PREFIX_1BYTE = 0xef; +const PREFIX_2BYTE = 0xfc; +const PREFIX_4BYTE = 0xfd; +const PREFIX_8BYTE = 0xfe; +const MAX_1BYTE = new BN(PREFIX_1BYTE); +const MAX_2BYTE = new BN(1).shln(16); +const MAX_4BYTE = new BN(1).shln(32); + +/** + * @name AccountIndex + * @description + * A wrapper around an AccountIndex, which is a shortened, variable-length encoding + * for an Account. We extends from [[U32]] to provide the number-like properties. + */ +export default class AccountIndex extends U32 { + constructor (value: AnyNumber = new BN(0)) { + super( + AccountIndex.decodeAccountIndex(value) + ); + } + + static decodeAccountIndex (value: AnyNumber): BN | Uint8Array | number | string { + if (value instanceof AccountIndex) { + // `value.toBn()` on AccountIndex returns a pure BN (i.e. not an + // AccountIndex), which has the initial `toString()` implementation. + return value.toBn(); + } else if (isBn(value) || isNumber(value) || isHex(value) || isU8a(value)) { + return value; + } + + return AccountIndex.decodeAccountIndex(decodeAddress(value)); + } + + static calcLength (_value: BN | number): number { + const value = bnToBn(_value); + + if (value.lte(MAX_1BYTE)) { + return 1; + } else if (value.lt(MAX_2BYTE)) { + return 2; + } else if (value.lt(MAX_4BYTE)) { + return 4; + } + + return 8; + } + + static readLength (input: Uint8Array): [number, number] { + const first = input[0]; + + if (first === PREFIX_2BYTE) { + return [1, 2]; + } else if (first === PREFIX_4BYTE) { + return [1, 4]; + } else if (first === PREFIX_8BYTE) { + return [1, 8]; + } + + return [0, 1]; + } + + static writeLength (input: Uint8Array): Uint8Array { + switch (input.length) { + case 2: return new Uint8Array([PREFIX_2BYTE]); + case 4: return new Uint8Array([PREFIX_4BYTE]); + case 8: return new Uint8Array([PREFIX_8BYTE]); + default: return new Uint8Array([]); + } + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + // shortcut for BN or Number, don't create an object + if (isBn(other) || isNumber(other)) { + return super.eq(other); + } + + // convert and compare + return super.eq(new AccountIndex(other)); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toString(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + const length = AccountIndex.calcLength(this); + + return encodeAddress(this.toU8a().subarray(0, length)); + } +} diff --git a/packages/types/src/type/AccountInfo.ts b/packages/types/src/type/AccountInfo.ts new file mode 100644 index 0000000000..e0f0d1c367 --- /dev/null +++ b/packages/types/src/type/AccountInfo.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import Bytes from '../primitive/Bytes'; +import U64 from '../primitive/U64'; + +/** + * @name AccountInfo + * @description + * An Account information structure for contracts + */ +export default class AccountInfo extends Struct { + constructor (value: any) { + super({ + trieId: Bytes, + currentMemStored: U64 + }, value); + } + + /** + * @description The size of stored value in octet + */ + get currentMemStored (): U64 { + return this.get('currentMemStored') as U64; + } + + /** + * @description Unique ID for the subtree encoded as a byte + */ + get trieId (): Bytes { + return this.get('trieId') as Bytes; + } +} diff --git a/packages/types/src/type/Address.spec.ts b/packages/types/src/type/Address.spec.ts new file mode 100644 index 0000000000..18e4f43629 --- /dev/null +++ b/packages/types/src/type/Address.spec.ts @@ -0,0 +1,118 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import AccountId from './AccountId'; +import AccountIndex from './AccountIndex'; +import Address from './Address'; + +describe('Address', () => { + const testDecode = (type: string, input: Address | AccountId | AccountIndex | Array | Uint8Array, expected: string) => + it(`can decode from ${type}`, () => { + const a = new Address(input); + expect(a.toString()).toBe(expected); + }); + + describe('utility', () => { + it('equals on AccountId', () => { + const addr = '5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'; + + expect(new Address(addr).eq(addr)).toBe(true); + }); + + it('equals on AccountIndex', () => { + // see the test below - these are equivalent (with different prefix encoding) + expect(new Address('2jpAFn').eq('25GUyv')).toBe(true); + }); + }); + + describe('decoding', () => { + testDecode( + 'Address', + new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode( + 'AccountId', + new AccountId('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode( + 'AccountIndex (mixed prefixes)', + new Address('2jpAFn'), + // NOTE Expected adress here is encoded with prefix 42, input above with 68 + '25GUyv' + ); + testDecode( + 'AccountIndex (hex)', + new AccountIndex('0x0100'), + '25GUyv' + ); + testDecode( + 'Array', + [ + 255, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ], + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode( + 'Uint8Array (with prefix 255)', + Uint8Array.from([ + 255, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ]), + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testDecode( + 'Uint8Array (with prefix 1 byte)', + Uint8Array.from([1]), + 'F7NZ' + ); + testDecode( + 'Uint8Array (with prefix 2 bytes)', + Uint8Array.from([0xfc, 0, 1]), + '25GUyv' + ); + testDecode( + 'Uint8Array (with prefix 4 bytes)', + Uint8Array.from([0xfd, 17, 18, 19, 20]), + 'Mwz15xP2' + ); + // FIXME The specification allows for 8 byte addresses, however since AccountIndex is u32 internally + // (and defined that way in thd efault Substrate),this does not actually work since it is 8 bytes, + // instead of 4 bytes max u32 length + // testDecode( + // 'Uint8Array (with prefix 8 bytes)', + // Uint8Array.from([0xfe, 17, 18, 19, 20, 21, 22, 23, 24]), + // '3N5RJXxM5fLd4h' + // ); + }); + + describe('encoding', () => { + const testEncode = (to: 'toHex' | 'toString' | 'toU8a', expected: string | Uint8Array) => + it(`can encode ${to}`, () => { + const a = new Address('5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF'); + expect(a[to]()).toEqual(expected); + }); + + testEncode( + 'toHex', + '0xff0102030405060708010203040506070801020304050607080102030405060708' + ); + testEncode( + 'toString', + '5C62W7ELLAAfix9LYrcx5smtcffbhvThkM5x7xfMeYXCtGwF' + ); + testEncode( + 'toU8a', + Uint8Array.from([ + 255, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 + ]) + ); + }); +}); diff --git a/packages/types/src/type/Address.ts b/packages/types/src/type/Address.ts new file mode 100644 index 0000000000..af170e6209 --- /dev/null +++ b/packages/types/src/type/Address.ts @@ -0,0 +1,139 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { decodeAddress } from '@plugnet/keyring'; +import { hexToU8a, isBn, isHex, isNumber, isU8a, u8aConcat, u8aToHex, u8aToU8a, u8aToBn } from '@plugnet/util'; + +import { Codec } from '../types'; +import Base from '../codec/Base'; +import AccountId from './AccountId'; +import AccountIndex from './AccountIndex'; + +type AnyAddress = BN | Address | AccountId | AccountIndex | Array | Uint8Array | number | string; + +export const ACCOUNT_ID_PREFIX = new Uint8Array([0xff]); + +/** + * @name Address + * @description + * A wrapper around an AccountId and/or AccountIndex that is encoded with a prefix. + * Since we are dealing with underlying publicKeys (or shorter encoded addresses), + * we extend from Base with an AccountId/AccountIndex wrapper. Basically the Address + * is encoded as `[ , ...publicKey/...bytes ]` as per spec + */ +export default class Address extends Base implements Codec { + constructor (value: AnyAddress = new Uint8Array()) { + super( + Address.decodeAddress(value) + ); + } + + static decodeAddress (value: AnyAddress): AccountId | AccountIndex { + if (value instanceof AccountId || value instanceof AccountIndex) { + return value; + } else if (isBn(value) || isNumber(value)) { + return new AccountIndex(value); + } else if (value instanceof Address) { + return value.raw; + } else if (Array.isArray(value)) { + return Address.decodeAddress(u8aToU8a(value)); + } else if (isU8a(value)) { + // This allows us to instantiate an address with a raw publicKey. Do this first before + // we checking the first byte, otherwise we may split an already-existent valid address + if (value.length === 32) { + return new AccountId(value); + } else if (value[0] === 0xff) { + return new AccountId(value.subarray(1)); + } + + const [offset, length] = AccountIndex.readLength(value); + + return new AccountIndex(u8aToBn(value.subarray(offset, offset + length), true)); + } else if (isHex(value)) { + return Address.decodeAddress(hexToU8a(value)); + } + + const decoded = decodeAddress(value); + + return decoded.length === 32 + ? new AccountId(decoded) + : new AccountIndex(u8aToBn(decoded, true)); + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + const rawLength = this.rawLength; + + return rawLength + ( + // for 1 byte AccountIndexes, we are not adding a specific prefix + rawLength > 1 + ? 1 + : 0 + ); + } + + /** + * @description Checks if the value is an empty value + */ + get isEmpty (): boolean { + return this.raw.isEmpty; + } + + /** + * @description The length of the raw value, either AccountIndex or AccountId + */ + get rawLength (): number { + return this.raw instanceof AccountIndex + ? AccountIndex.calcLength(this.raw) + : this.raw.encodedLength; + } + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean { + return this.raw.eq(other); + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.raw.toJSON(); + } + + /** + * @description Returns the string representation of the value + */ + toString (): string { + return this.raw.toString(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + const encoded = this.raw.toU8a().subarray(0, this.rawLength); + + return isBare + ? encoded + : u8aConcat( + this.raw instanceof AccountIndex + ? AccountIndex.writeLength(encoded) + : ACCOUNT_ID_PREFIX, + encoded + ); + } +} diff --git a/packages/types/src/type/Amount.ts b/packages/types/src/type/Amount.ts new file mode 100644 index 0000000000..3c79d9b5f7 --- /dev/null +++ b/packages/types/src/type/Amount.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Balance from './Balance'; + +/** + * @name Amount + * @description + * The Substrate Amount representation as a [[Balance]]. + */ +export default class Amount extends Balance { +} diff --git a/packages/types/src/type/AssetOf.ts b/packages/types/src/type/AssetOf.ts new file mode 100644 index 0000000000..69dca3bc93 --- /dev/null +++ b/packages/types/src/type/AssetOf.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Balance from './Balance'; + +/** + * @name AssetOf + * @description + * The Substrate AssetOf representation as a [[Balance]]. + */ +export default class AssetOf extends Balance { +} diff --git a/packages/types/src/type/AttestedCandidate.ts b/packages/types/src/type/AttestedCandidate.ts new file mode 100644 index 0000000000..47097b1f82 --- /dev/null +++ b/packages/types/src/type/AttestedCandidate.ts @@ -0,0 +1,96 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../codec/EnumType'; +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import Vector from '../codec/Vector'; +import Bytes from '../primitive/Bytes'; +import Null from '../primitive/Null'; +import U64 from '../primitive/U64'; +import AccountId from './AccountId'; +import Hash from './Hash'; +import ParaId from './ParaId'; +import Signature from './Signature'; +import SessionKey from './SessionKey'; + +export class CandidateSignature extends Signature { +} + +export class BalanceUpload extends Tuple.with({ AccountId, U64 }) { +} + +export class EgressQueueRoot extends Tuple.with({ ParaId, Hash }) { +} + +export class HeadData extends Bytes { +} + +export class CandidateReceipt extends Struct { + constructor (value?: any) { + super({ + parachainIndex: ParaId, + collator: AccountId, + signature: CandidateSignature, + headData: HeadData, + balanceUploads: Vector.with(BalanceUpload), + egressQueueRoots: Vector.with(EgressQueueRoot), + fees: U64, + blockDataHash: Hash + }, value); + } +} + +export class AvailabilityVote extends Tuple.with({ SessionKey, CandidateSignature }) { +} + +export class ExplicitCandidateSignature extends CandidateSignature { +} + +export class ImplicitCandidateSignature extends CandidateSignature { +} + +export class ValidityAttestation extends EnumType { + constructor (value?: any) { + super({ + // This Null is not in the original, however indexes start at 1, so add a + // placeholder in the first position (which is basically non-valid) + Null, + ImplicitCandidateSignature, // 1 + ExplicitCandidateSignature // 2 + }, value); + } + + /** + * @description Returns the item as a [[ExplicitCandidateSignature]] + */ + get asExplicitCandidateSignature (): ExplicitCandidateSignature { + return this.value as ExplicitCandidateSignature; + } + + /** + * @description Returns the item as a [[ImplicitCandidateSignature]] + */ + get asImplicitCandidateSignature (): ImplicitCandidateSignature { + return this.value as ImplicitCandidateSignature; + } +} + +export class ValidityVote extends Tuple.with({ SessionKey, ValidityAttestation }) { +} + +/** + * @name AttestedCandidate + * @description + * An attested candidate + */ +export default class AttestedCandidate extends Struct { + constructor (value?: any) { + super({ + candidate: CandidateReceipt, + validityVotes: Vector.with(ValidityVote), + availabilityVotes: Vector.with(AvailabilityVote) + }, value); + } +} diff --git a/packages/types/src/type/AuthorityId.ts b/packages/types/src/type/AuthorityId.ts new file mode 100644 index 0000000000..f8bf6dd3ef --- /dev/null +++ b/packages/types/src/type/AuthorityId.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import AccountId from './AccountId'; + +// Wrapper for a AuthorityId. Same as an normal AccountId, i.e. a wrapper +// around publicKey, however specialized since it specifically points to +// an authority. +export default class AuthorityId extends AccountId { +} diff --git a/packages/types/src/type/Balance.ts b/packages/types/src/type/Balance.ts new file mode 100644 index 0000000000..1f25301f09 --- /dev/null +++ b/packages/types/src/type/Balance.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U128 from '../primitive/U128'; + +/** + * @name Balance + * @description + * The Substrate Balance representation as a [[U128]]. + */ +export default class Balance extends U128 { +} + +/** + * @name BalanceOf + * @description + * The Substrate BalanceOf representation as a [[Balance]]. + */ +export class BalanceOf extends Balance { +} diff --git a/packages/types/src/type/BalanceLock.ts b/packages/types/src/type/BalanceLock.ts new file mode 100644 index 0000000000..46089dcefe --- /dev/null +++ b/packages/types/src/type/BalanceLock.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import Balance from './Balance'; +import BlockNumber from './BlockNumber'; +import LockIdentifier from './LockIdentifier'; +import WithdrawReasons from './WithdrawReasons'; + +/** + * @name BalanceLock + * @description + * The Substrate BalanceLock for staking + */ +export default class BalanceLock extends Struct { + constructor (value?: any) { + super({ + id: LockIdentifier, + amount: Balance, + until: BlockNumber, + reasons: WithdrawReasons + }, value); + } + + /** + * @description The amount + */ + get amount (): Balance { + return this.get('amount') as Balance; + } + + /** + * @description The lock id + */ + get id (): LockIdentifier { + return this.get('id') as LockIdentifier; + } + + /** + * @description The reasons + */ + get reasons (): WithdrawReasons { + return this.get('reasons') as WithdrawReasons; + } + + /** + * @description Until when this is available + */ + get until (): BlockNumber { + return this.get('until') as BlockNumber; + } +} diff --git a/packages/types/src/type/Bft.spec.ts b/packages/types/src/type/Bft.spec.ts new file mode 100644 index 0000000000..8a9936c44c --- /dev/null +++ b/packages/types/src/type/Bft.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BftAuthoritySignature, BftHashSignature } from './Bft'; + +describe('BftAuthoritySignature', () => { + const sig = new BftAuthoritySignature([ + '0x12344321', + '0x567890098765' + ]); + + it('has the correct authorityId', () => { + expect(sig.authorityId.toHex()).toEqual('0x1234432100000000000000000000000000000000000000000000000000000000'); + }); + + it('has the correct signature', () => { + expect(sig.signature.toHex()).toEqual('0x56789009876500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); + }); +}); + +describe('BftHashSignature', () => { + const sig = new BftHashSignature([ + '0x12344321', + '0x567890098765' + ]); + + it('has the correct hash', () => { + expect(sig.hash.toHex()).toEqual('0x1234432100000000000000000000000000000000000000000000000000000000'); + }); + + it('has the correct signature', () => { + expect(sig.signature.toHex()).toEqual('0x56789009876500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); + }); +}); diff --git a/packages/types/src/type/Bft.ts b/packages/types/src/type/Bft.ts new file mode 100644 index 0000000000..09f73bb1ba --- /dev/null +++ b/packages/types/src/type/Bft.ts @@ -0,0 +1,72 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import Tuple from '../codec/Tuple'; +import AuthorityId from './AuthorityId'; +import Hash from './Hash'; +import Signature from './Signature'; + +export type BftAuthoritySignatureValue = [AnyU8a, AnyU8a]; + +/** + * @name BftAuthoritySignature + * @description + * Represents a Bft Hash and Signature pairing, typically used in reporting + * network behaviour. + */ +export class BftAuthoritySignature extends Tuple { + constructor (value?: BftAuthoritySignatureValue | Uint8Array) { + super({ + AuthorityId, + Signature + }, value); + } + + /** + * @description The wrapped [[AuthoriyId]] + */ + get authorityId (): AuthorityId { + return this[0] as AuthorityId; + } + + /** + * @description The wrapped [[Signature]] value + */ + get signature (): Signature { + return this[1] as Signature; + } +} + +export type BftHashSignatureValue = [AnyU8a, AnyU8a]; + +/** + * @name BftHashSignature + * @description + * Represents a Bft Hash and Signature pairing, typically used in reporting + * network behaviour. + */ +export class BftHashSignature extends Tuple { + constructor (value?: BftHashSignatureValue | Uint8Array) { + super({ + Hash, + Signature + }, value); + } + + /** + * @description The wrapped [[Hash]] + */ + get hash (): Hash { + return this[0] as Hash; + } + + /** + * @description The wrapped [[Signature]] + */ + get signature (): Signature { + return this[1] as Signature; + } +} diff --git a/packages/types/src/type/Block.ts b/packages/types/src/type/Block.ts new file mode 100644 index 0000000000..505ed1a30f --- /dev/null +++ b/packages/types/src/type/Block.ts @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import { blake2AsU8a } from '@plugnet/util-crypto'; + +import Struct from '../codec/Struct'; +import Extrinsics from './Extrinsics'; +import Hash from './Hash'; +import Header, { HeaderValue } from './Header'; + +export type BlockValue = { + extrinsics?: Array + header?: HeaderValue +}; + +/** + * @name Block + * @description + * A block encoded with header and extrinsics + */ +export default class Block extends Struct { + constructor (value?: BlockValue | Uint8Array) { + super({ + header: Header, + extrinsics: Extrinsics + }, value); + } + + /** + * @description Encodes a content [[Hash]] for the block + */ + get contentHash (): Hash { + return new Hash( + blake2AsU8a(this.toU8a(), 256) + ); + } + + /** + * @description The [[Extrinsics]] contained in the block + */ + get extrinsics (): Extrinsics { + return this.get('extrinsics') as Extrinsics; + } + + /** + * @description Block/header [[Hash]] + */ + get hash (): Hash { + return this.header.hash; + } + + /** + * @description The [[Header]] of the block + */ + get header (): Header { + return this.get('header') as Header; + } +} diff --git a/packages/types/src/type/BlockNumber.ts b/packages/types/src/type/BlockNumber.ts new file mode 100644 index 0000000000..1f8ef42c7a --- /dev/null +++ b/packages/types/src/type/BlockNumber.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U64 from '../primitive/U64'; + +/** + * @name BlockNumber + * @description + * A representation of a Substrate BlockNumber, implemented as a [[U64]] + */ +export default class BlockNumber extends U64 { +} diff --git a/packages/types/src/type/CodeHash.ts b/packages/types/src/type/CodeHash.ts new file mode 100644 index 0000000000..27b1fc8f74 --- /dev/null +++ b/packages/types/src/type/CodeHash.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Hash from './Hash'; + +/** + * @name CodeHash + * @description + * The default contract code hash that is used accross the system. It is a + * wrapper around [[Hash]], representing a 32-byte blake2b (Substrate) value + */ +export default class CodeHash extends Hash { +} diff --git a/packages/types/src/type/Digest.spec.ts b/packages/types/src/type/Digest.spec.ts new file mode 100644 index 0000000000..d7ab7fb1ee --- /dev/null +++ b/packages/types/src/type/Digest.spec.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import json from '../json/Header.002.json'; +import Digest from './Digest'; + +describe('Digest', () => { + it('decodes logs with seals', () => { + const digest = new Digest(json.result.digest); + + expect(digest.logs.length).toEqual(1); + + const log = digest.logs[0]; + + expect(log.type).toBe('Seal'); + expect(log.value.toJSON()).toEqual([ + 256804359, + '0x2c0e0ee8a5d1a073da3f8db002b8eaebcd7b1a7eb25662e09f9ef0ac58d96bf17896256cd5f0bc672f96aab49fe5163ca5cb4c1a0f047dc39e89cd4a9eb2ea05' + ]); + }); +}); diff --git a/packages/types/src/type/Digest.ts b/packages/types/src/type/Digest.ts new file mode 100644 index 0000000000..e03efbe93e --- /dev/null +++ b/packages/types/src/type/Digest.ts @@ -0,0 +1,167 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../codec/EnumType'; +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import Vector from '../codec/Vector'; +import Bytes from '../primitive/Bytes'; +import U32 from '../primitive/U32'; +import U64 from '../primitive/U64'; +import AuthorityId from './AuthorityId'; +import Hash from './Hash'; +import Signature from './Signature'; + +/** + * @name AuthoritiesChange + * @description + * Log for Authories changed + */ +export class AuthoritiesChange extends Vector.with(AuthorityId) { +} + +/** + * @name ChangesTrieRoot + * @description + * Log for changes to the Trie root + */ +export class ChangesTrieRoot extends Hash { +} + +/** + * @name Other + * @description + * Log item that is just a stream of [[Bytes]] + */ +export class Other extends Bytes { +} + +/** + * @name Seal + * @description + * Log item indicating a sealing event + */ +export class Seal extends Tuple { + constructor (value: any) { + super({ + U64, + Signature + }, value); + } + + /** + * @description The wrapped [[Signature]] + */ + get signature (): Signature { + return this[1] as Signature; + } + + /** + * @description The wrapped [[U64]] slot + */ + get slot (): U64 { + return this[0] as U64; + } +} + +/** + * @name Consensus + * @description + * Log item indicating consensus + */ +export class Consensus extends Tuple { + constructor (value: any) { + super({ + U32, // actually a [u8; 4] + Bytes + }, value); + } +} + +/** + * @name DigestItem + * @description + * A [[EnumType]] the specifies the specific item in the logs of a [[Digest]] + */ +export class DigestItem extends EnumType { + constructor (value: any) { + super({ + Other, // Position 0, as per Rust (encoding control) + AuthoritiesChange, + ChangesTrieRoot, + Seal, + Consensus + }, value); + } + + /** + * @description Returns the item as a [[AuthoritiesChange]] + */ + get asAuthoritiesChange (): AuthoritiesChange { + return this.value as AuthoritiesChange; + } + + /** + * @description Returns the item as a [[ChangesTrieRoot]] + */ + get asChangesTrieRoot (): ChangesTrieRoot { + return this.value as ChangesTrieRoot; + } + + /** + * @desciption Retuns the item as a [[Consensus]] + */ + get asConsensus (): Consensus { + return this.value as Consensus; + } + + /** + * @description Returns the item as a [[Other]] + */ + get asOther (): Other { + return this.value as Other; + } + + /** + * @description Returns the item as a [[Seal]] + */ + get asSeal (): Seal { + return this.value as Seal; + } + + /** + * @description Returns true on [[Consensus]] + */ + get isConsensus (): boolean { + return this.type === 'Consensus'; + } + + /** + * @description Returns true on [[Seal]] + */ + get isSeal (): boolean { + return this.type === 'Seal'; + } +} + +/** + * @name Digest + * @description + * A [[Header]] Digest + */ +export default class Digest extends Struct { + constructor (value: any) { + super({ + logs: Vector.with(DigestItem) + }, value); + } + + /** + * @description The [[DigestItem]] logs + */ + get logs (): Vector { + return this.get('logs') as Vector; + } +} diff --git a/packages/types/src/type/Event.ts b/packages/types/src/type/Event.ts new file mode 100644 index 0000000000..2aa075e2a1 --- /dev/null +++ b/packages/types/src/type/Event.ts @@ -0,0 +1,182 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Constructor } from '../types'; + +import { isUndefined, stringCamelCase, u8aToHex } from '@plugnet/util'; + +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import U8aFixed from '../codec/U8aFixed'; +import Null from '../primitive/Null'; +import { TypeDef, getTypeClass, getTypeDef } from '../codec/createType'; +import MetadataV0 from '../Metadata/v0'; +import { EventMetadata } from '../Metadata/v0/Events'; + +const EventTypes: { [index: string]: Constructor } = {}; + +/** + * @name EventData + * @description + * Wrapper for the actual data that forms part of an [[Event]] + */ +export class EventData extends Tuple { + private _meta: EventMetadata; + private _method: string; + private _section: string; + private _typeDef: Array; + + constructor (Types: Array, value: Uint8Array, typeDef: Array, meta: EventMetadata, section: string, method: string) { + super(Types, value); + + this._meta = meta; + this._method = method; + this._section = section; + this._typeDef = typeDef; + } + + /** + * @description The wrapped [[EventMetadata]] + */ + get meta (): EventMetadata { + return this._meta; + } + + /** + * @description The method as a string + */ + get method (): string { + return this._method; + } + + /** + * @description The section as a string + */ + get section (): string { + return this._section; + } + + /** + * @description The [[TypeDef]] for this event + */ + get typeDef (): Array { + return this._typeDef; + } +} + +/** + * @name EventIndex + * @description + * This follows the same approach as in [[Method]], we have the `[sectionIndex, methodIndex]` pairing + * that indicates the actual event fired + */ +export class EventIndex extends U8aFixed { + constructor (value?: any) { + super(value, 16); + } +} + +/** + * @name Event + * @description + * A representation of a system event. These are generated via the [[Metadata]] interfaces and + * specific to a specific Substrate runtime + */ +export default class Event extends Struct { + // Currently we _only_ decode from Uint8Array, since we expect it to + // be used via EventRecord + constructor (_value?: Uint8Array) { + const { DataType, value } = Event.decodeEvent(_value); + + super({ + index: EventIndex, + data: DataType + }, value); + } + + static decodeEvent (value: Uint8Array = new Uint8Array()) { + const index = value.subarray(0, 2); + const DataType = EventTypes[index.toString()]; + + if (isUndefined(DataType)) { + console.error(`Unable to decode event for index ${u8aToHex(index)}`); + + return { + DataType: Null, + value: undefined + }; + } + + return { + DataType, + value: { + index, + data: value.subarray(2) + } + }; + } + + // This is called/injected by the API on init, allowing a snapshot of + // the available system events to be used in lookups + static injectMetadata (metadata: MetadataV0): void { + metadata.events.forEach((section, sectionIndex) => { + const sectionName = stringCamelCase(section.name.toString()); + + section.events.forEach((meta, methodIndex) => { + const methodName = meta.name.toString(); + const eventIndex = new Uint8Array([sectionIndex, methodIndex]); + const typeDef = meta.arguments.map((arg) => getTypeDef(arg)); + const Types = typeDef.map(getTypeClass); + + EventTypes[eventIndex.toString()] = class extends EventData { + constructor (value: Uint8Array) { + super(Types, value, typeDef, meta, sectionName, methodName); + } + }; + }); + }); + } + + /** + * @description The wrapped [[EventData]] + */ + get data (): EventData { + return this.get('data') as EventData; + } + + /** + * @description The [[EventIndex]], identifying the raw event + */ + get index (): EventIndex { + return this.get('index') as EventIndex; + } + + /** + * @description The [[EventMetadata]] with the documentation + */ + get meta (): EventMetadata { + return this.data.meta; + } + + /** + * @description The method string identifying the event + */ + get method (): string { + return this.data.method; + } + + /** + * @description The section string identifying the event + */ + get section (): string { + return this.data.section; + } + + /** + * @description The [[TypeDef]] for the event + */ + get typeDef (): Array { + return this.data.typeDef; + } +} diff --git a/packages/types/src/type/EventRecord.spec.ts b/packages/types/src/type/EventRecord.spec.ts new file mode 100644 index 0000000000..9137bc7510 --- /dev/null +++ b/packages/types/src/type/EventRecord.spec.ts @@ -0,0 +1,67 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import createType from '../codec/createType'; +import Vector from '../codec/Vector'; +import json1 from '../json/EventRecord.001.json'; +import json2 from '../json/EventRecord.002.json'; +import json3 from '../json/EventRecord.003.json'; +import AccountId from './AccountId'; +import Event from './Event'; +import EventRecord from './EventRecord'; +import Metadata from '../Metadata'; +import metadataV0 from '../Metadata/v0/static'; +import metadataV1 from '../Metadata/static'; + +describe('EventRecord', () => { + describe('v0', () => { + beforeEach(() => { + Event.injectMetadata( + new Metadata(metadataV0).asV0 + ); + }); + + it('decodes correctly', () => { + const records: Vector = createType('Vec', json1.params.result.changes[0][1]) as any; + const er = records[0]; + + expect(er.phase.type).toEqual('ApplyExtrinsic'); + }); + + // FIXME skipping this one, need an actual updated sample for the actual new types + it.skip('decodes more complex events', () => { + const records: Vector = createType('Vec', json2.params.result.changes[0][1]) as any; + + expect(records).toHaveLength(4); + + const er = records[2]; + + expect( + er.event.data.toArray().map((v) => v.toString()) + ).toEqual( + [ + new AccountId('0xd2de7394ae047a5502ad9adb9cc69ff6fe484033bfce874d775da947487cd832').toString(), + new AccountId('0x37e027d776cd005c12bcf6722421374a9037167a0ceaf918f341c4ad68d54e59').toString(), + '1000', + '0' + ] + ); + }); + }); + + describe.skip('v1', () => { + beforeEach(() => { + Event.injectMetadata( + new Metadata(metadataV1).asV0 + ); + }); + + it('decodes correctly', () => { + const records: Vector = createType('Vec', json3.params.result.changes[0][1]) as any; + const er = records[0]; + + expect(er.phase.type).toEqual('ApplyExtrinsic'); + }); + }); +}); diff --git a/packages/types/src/type/EventRecord.ts b/packages/types/src/type/EventRecord.ts new file mode 100644 index 0000000000..7d8e83d16d --- /dev/null +++ b/packages/types/src/type/EventRecord.ts @@ -0,0 +1,96 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import EnumType from '../codec/EnumType'; +import Struct from '../codec/Struct'; +import Null from '../primitive/Null'; +import U32 from '../primitive/U32'; +import Event from './Event'; + +/** + * @name ApplyExtrinsic + * @description + * The [[Phase]] where the extrinsic is applied + */ +export class ApplyExtrinsic extends U32 { +} + +/** + * @name Finalization + * @description + * The [[Phase]] where the extrinsic is being Finalized + */ +export class Finalization extends Null { +} + +/** + * @name Phase + * @description + * An [[EnumType]] that indicates the specific phase where the [[EventRecord]] was generated + */ +export class Phase extends EnumType { + constructor (value: any, index?: number) { + super({ + ApplyExtrinsic, + Finalization + }, value, index); + } + + /** + * @description Returns the item as a [[ApplyExtrinsic]] + */ + get asApplyExtrinsic (): ApplyExtrinsic { + return this.value as ApplyExtrinsic; + } + + /** + * @description Returns the item as a [[Finalization]] + */ + get asFinalization (): Finalization { + return this.value as Finalization; + } + + /** + * @description true when this is a ApplyExtrinsic + */ + get isApplyExtrinsic (): boolean { + return this.type === 'ApplyExtrinsic'; + } + + /** + * @description true when this is a ApplyExtrinsic + */ + get isFinalization (): boolean { + return this.type === 'Finalization'; + } +} + +/** + * @name EventRecord + * @description + * A record for an [[Event]] (as specified by [[Metadata]]) with the specific [[Phase]] of + * application. + */ +export default class EventRecord extends Struct { + constructor (value: any) { + super({ + phase: Phase, + event: Event + }, value); + } + + /** + * @description The [[Event]] this record refers to + */ + get event (): Event { + return this.get('event') as Event; + } + + /** + * @description The [[Phase]] where the event was generated + */ + get phase (): Phase { + return this.get('phase') as Phase; + } +} diff --git a/packages/types/src/type/Exposure.ts b/packages/types/src/type/Exposure.ts new file mode 100644 index 0000000000..96ca588877 --- /dev/null +++ b/packages/types/src/type/Exposure.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import Vector from '../codec/Vector'; +import Balance from './Balance'; +import IndividualExposure from './IndividualExposure'; + +/** + * @name Exposure + * @description + * A snapshot of the stake backing a single validator in the system + */ +export default class Exposure extends Struct { + constructor (value?: any) { + super({ + total: Compact.with(Balance), + own: Compact.with(Balance), + others: Vector.with(IndividualExposure) + }, value); + } + + /** + * @description The validator's own stash that is exposed + */ + get own (): Balance { + return (this.get('own') as Compact).toBn() as Balance; + } + + /** + * @description The total balance backing this validator + */ + get total (): Balance { + return (this.get('total') as Compact).toBn() as Balance; + } + + /** + * @description The portions of nominators stashes that are exposed + */ + get others (): Vector { + return this.get('others') as Vector; + } +} diff --git a/packages/types/src/type/Extrinsic.spec.ts b/packages/types/src/type/Extrinsic.spec.ts new file mode 100644 index 0000000000..93716c066f --- /dev/null +++ b/packages/types/src/type/Extrinsic.spec.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import extrinsics from '@plugnet/extrinsics/static'; + +import Method from '../primitive/Method'; +import Extrinsic from './Extrinsic'; + +describe('Extrinsic', () => { + beforeAll(() => { + Method.injectMethods(extrinsics); + }); + + it.skip('decodes a non-signed properly via JSON', () => { + const extrinsic = new Extrinsic('0x010200ea51b75b00000000'); + + expect(extrinsic.isSigned).toEqual(false); + expect(extrinsic.callIndex).toEqual(new Uint8Array([2, 0])); + expect(extrinsic.data).toEqual(new Uint8Array([234, 81, 183, 91, 0, 0, 0, 0])); + }); + + it('decodes an actual transaction (new format)', () => { + const extrinsic = new Extrinsic('0x250281ffbfc823aa75c30058eeec21abe2c2d6b7247418a4af89d67a2084c2ac864da080c0aa4df3b4926c3cd78bbdced31d8bdccb8604b779b71b90e58b2848df4a9ad6b0aa1aae6be7a05c9413a172b0325e4d214e5ff2b25098028b30f1a50be9c90e0c000300ff4a83f1c09be797bc3d9adce29818368b276a84e6b545ced492c25c948978d7f8e5c0'); + + expect(extrinsic.isSigned).toEqual(true); + expect(extrinsic.signature.signer.toU8a()).toEqual(new Uint8Array([255, 191, 200, 35, 170, 117, 195, 0, 88, 238, 236, 33, 171, 226, 194, 214, 183, 36, 116, 24, 164, 175, 137, 214, 122, 32, 132, 194, 172, 134, 77, 160, 128])); + expect(extrinsic.signature.signature.toHex()).toEqual('0xc0aa4df3b4926c3cd78bbdced31d8bdccb8604b779b71b90e58b2848df4a9ad6b0aa1aae6be7a05c9413a172b0325e4d214e5ff2b25098028b30f1a50be9c90e'); + expect(extrinsic.signature.nonce.toNumber()).toEqual(3); + expect(extrinsic.signature.era.toU8a()).toEqual(new Uint8Array([0])); + expect(extrinsic.callIndex).toEqual(new Uint8Array([3, 0])); + expect(extrinsic.args[0].toString()).toEqual('5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'); + }); + + it('decodes an actual transaction (old format)', () => { + const extrinsic = new Extrinsic( + '0x81ffbfc823aa75c30058eeec21abe2c2d6b7247418a4af89d67a2084c2ac864da080c0aa4df3b4926c3cd78bbdced31d8bdccb8604b779b71b90e58b2848df4a9ad6b0aa1aae6be7a05c9413a172b0325e4d214e5ff2b25098028b30f1a50be9c90e0c000300ff4a83f1c09be797bc3d9adce29818368b276a84e6b545ced492c25c948978d7f8e5c0' + ); + + expect(extrinsic.isSigned).toEqual(true); + expect(extrinsic.signature.signer.toU8a()).toEqual(new Uint8Array([255, 191, 200, 35, 170, 117, 195, 0, 88, 238, 236, 33, 171, 226, 194, 214, 183, 36, 116, 24, 164, 175, 137, 214, 122, 32, 132, 194, 172, 134, 77, 160, 128])); + expect(extrinsic.signature.signature.toHex()).toEqual('0xc0aa4df3b4926c3cd78bbdced31d8bdccb8604b779b71b90e58b2848df4a9ad6b0aa1aae6be7a05c9413a172b0325e4d214e5ff2b25098028b30f1a50be9c90e'); + expect(extrinsic.signature.nonce.toNumber()).toEqual(3); + expect(extrinsic.signature.era.toU8a()).toEqual(new Uint8Array([0])); + expect(extrinsic.callIndex).toEqual(new Uint8Array([3, 0])); + expect(extrinsic.args[0].toString()).toEqual('5DkQbYAExs3M2sZgT1Ec3mKfZnAQCL4Dt9beTCknkCUn5jzo'); + }); +}); diff --git a/packages/types/src/type/Extrinsic.ts b/packages/types/src/type/Extrinsic.ts new file mode 100644 index 0000000000..bd2518e550 --- /dev/null +++ b/packages/types/src/type/Extrinsic.ts @@ -0,0 +1,204 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import { AnyNumber, AnyU8a, ArgsDef, Codec, IExtrinsic, SignatureOptions } from '../types'; + +import { isHex, isU8a, u8aToHex, u8aToU8a } from '@plugnet/util'; +import { blake2AsU8a } from '@plugnet/util-crypto'; + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import { FunctionMetadata } from '../Metadata/v0/Modules'; +import Method from '../primitive/Method'; +import Address from './Address'; +import ExtrinsicSignature from './ExtrinsicSignature'; +import Hash from './Hash'; + +type ExtrinsicValue = { + method?: Method + signature?: ExtrinsicSignature +}; + +/** + * @name Extrinsic + * @description + * Representation of an Extrinsic in the system. It contains the actual call, + * (optional) signature and encodes with an actual length prefix + * + * {@link https://github.com/paritytech/wiki/blob/master/Extrinsic.md#the-extrinsic-format-for-node}. + * + * Can be: + * - signed, to create a transaction + * - left as is, to create an inherent + */ +export default class Extrinsic extends Struct implements IExtrinsic { + constructor (value?: ExtrinsicValue | AnyU8a | Method) { + super({ + signature: ExtrinsicSignature, + method: Method + }, Extrinsic.decodeExtrinsic(value || {})); + } + + static decodeExtrinsic (value: ExtrinsicValue | AnyU8a | Method): ExtrinsicValue | Array | Uint8Array { + if (Array.isArray(value) || isHex(value)) { + // Instead of the block below, it should simply be: + // return Extrinsic.decodeExtrinsic(hexToU8a(value as string)); + const u8a = u8aToU8a(value); + + // HACK 11 Jan 2019 - before https://github.com/paritytech/substrate/pull/1388 + // extrinsics didn't have the length, cater for both approaches + const [offset, length] = Compact.decodeU8a(u8a); + const withPrefix = u8a.length === (offset + length.toNumber()); + + return Extrinsic.decodeExtrinsic( + withPrefix + ? u8a + : Compact.addLengthPrefix(u8a) + ); + } else if (isU8a(value)) { + const [offset, length] = Compact.decodeU8a(value); + + return value.subarray(offset, offset + length.toNumber()); + } else if (value instanceof Method) { + return { + method: value + }; + } + + return value; + } + + /** + * @description The arguments passed to for the call, exposes args so it is compatible with [[Method]] + */ + get args (): Array { + return this.method.args; + } + + /** + * @description Thge argument defintions, compatible with [[Method]] + */ + get argsDef (): ArgsDef { + return this.method.argsDef; + } + + /** + * @description The actual `[sectionIndex, methodIndex]` as used in the Method + */ + get callIndex (): Uint8Array { + return this.method.callIndex; + } + + /** + * @description The actual data for the Method + */ + get data (): Uint8Array { + return this.method.data; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + const length = this.length; + + return length + Compact.encodeU8a(length).length; + } + + /** + * @description Convernience function, encodes the extrinsic and returns the actual hash + */ + get hash (): Hash { + return new Hash( + blake2AsU8a(this.toU8a(), 256) + ); + } + + /** + * @description `true` is method has `Origin` argument (compatibility with [[Method]]) + */ + get hasOrigin (): boolean { + return this.method.hasOrigin; + } + + /** + * @description `true` id the extrinsic is signed + */ + get isSigned (): boolean { + return this.signature.isSigned; + } + + /** + * @description The length of the encoded value + */ + get length (): number { + return this.toU8a(true).length; + } + + /** + * @description The [[FunctionMetadata]] that describes the extrinsic + */ + get meta (): FunctionMetadata { + return this.method.meta; + } + + /** + * @description The [[Method]] this extrinsic wraps + */ + get method (): Method { + return this.get('method') as Method; + } + + /** + * @description The [[ExtrinsicSignature]] + */ + get signature (): ExtrinsicSignature { + return this.get('signature') as ExtrinsicSignature; + } + + /** + * @description Add an [[ExtrinsicSignature]] to the extrinsic (already generated) + */ + addSignature (signer: Address | Uint8Array, signature: Uint8Array, nonce: AnyNumber, era?: Uint8Array): Extrinsic { + this.signature.addSignature(signer, signature, nonce, era); + + return this; + } + + /** + * @description Sign the extrinsic with a specific keypair + */ + sign (account: KeyringPair, options: SignatureOptions): Extrinsic { + this.signature.sign(this.method, account, options); + + return this; + } + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string { + return u8aToHex(this.toU8a()); + } + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any { + return this.toHex(); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + const encoded = super.toU8a(); + + return isBare + ? encoded + : Compact.addLengthPrefix(encoded); + } +} diff --git a/packages/types/src/type/ExtrinsicEra.ts b/packages/types/src/type/ExtrinsicEra.ts new file mode 100644 index 0000000000..2b0683fe53 --- /dev/null +++ b/packages/types/src/type/ExtrinsicEra.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import { u8aToU8a } from '@plugnet/util'; + +import U8a from '../codec/U8a'; + +/** + * @name ExtrinsicEra + * @description + * The era for an extrinsic, indicating either a mortal or immortal extrinsic + */ +export default class ExtrinsicEra extends U8a { + constructor (value?: AnyU8a) { + super( + ExtrinsicEra.decodeExtrinsicEra(value) + ); + } + + static decodeExtrinsicEra (value?: AnyU8a): Uint8Array { + if (value) { + const u8a = u8aToU8a(value); + + // If we have a zero byte, it is immortal (1 byte in length), otherwise we have + // the era details following as another byte + return u8a.subarray(0, (u8a[0] === 0) ? 1 : 2); + } + + return new Uint8Array([0]); + } +} diff --git a/packages/types/src/type/ExtrinsicSignature.ts b/packages/types/src/type/ExtrinsicSignature.ts new file mode 100644 index 0000000000..def490dbe2 --- /dev/null +++ b/packages/types/src/type/ExtrinsicSignature.ts @@ -0,0 +1,165 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import { AnyNumber, IExtrinsicSignature, SignatureOptions } from '../types'; + +import Struct from '../codec/Struct'; +import Address from './Address'; +import Method from '../primitive/Method'; +import U8 from '../primitive/U8'; +import ExtrinsicEra from './ExtrinsicEra'; +import Nonce from './NonceCompact'; +import RuntimeVersion from '../rpc/RuntimeVersion'; +import Signature from './Signature'; +import SignaturePayload from './SignaturePayload'; + +export const IMMORTAL_ERA = new Uint8Array([0]); + +const BIT_SIGNED = 0b10000000; +const BIT_UNSIGNED = 0; +const BIT_VERSION = 0b0000001; + +/** + * @name ExtrinsicSignature + * @description + * A container for the [[Signature]] associated with a specific [[Extrinsic]] + */ +export default class ExtrinsicSignature extends Struct implements IExtrinsicSignature { + // Signature Information. + // 1 byte version: BIT_VERSION | (isSigned ? BIT_SIGNED : BIT_UNSIGNED) + // 1/3/5/9/33 bytes: The signing account identity, in Address format + // 64 bytes: The Ed25519 signature of the Signing Payload + // 8 bytes: The Transaction Index of the signing account + // 1/2 bytes: The Transaction Era + constructor (value?: Uint8Array) { + super({ + version: U8, + signer: Address, + signature: Signature, + nonce: Nonce, + era: ExtrinsicEra + }, ExtrinsicSignature.decodeExtrinsicSignature(value)); + } + + static decodeExtrinsicSignature (value?: Uint8Array): object | Uint8Array { + if (!value) { + return { + // we always explicitly set the unsigned version + version: BIT_VERSION | BIT_UNSIGNED + }; + } + + const version = value[0]; + + // only decode the full Uint8Array if we have the signed indicator, + // alternatively only return the version (default for others) + return (version & BIT_SIGNED) === BIT_SIGNED + ? value + : { version }; + } + + /** + * @description The length of the value when encoded as a Uint8Array + */ + get encodedLength (): number { + return this.isSigned + ? super.encodedLength + : 1; + } + + /** + * @description `true` if the signature is valid + */ + get isSigned (): boolean { + return (this.version & BIT_SIGNED) === BIT_SIGNED; + } + + /** + * @description The [[ExtrinsicEra]] (mortal or immortal) this signature applies to + */ + get era (): ExtrinsicEra { + return this.get('era') as ExtrinsicEra; + } + + /** + * @description The [[Nonce]] for the signature + */ + get nonce (): Nonce { + return this.get('nonce') as Nonce; + } + + /** + * @description The actuall [[Signature]] hash + */ + get signature (): Signature { + return this.get('signature') as Signature; + } + + /** + * @description The [[Address]] that signed + */ + get signer (): Address { + return this.get('signer') as Address; + } + + /** + * @description The encoded version for the signature + */ + get version (): number { + // Version Information. + // 1 byte: version information: + // - 7 low bits: version identifier (should be 0b0000001). + // - 1 high bit: signed flag: 1 if this is a transaction (e.g. contains a signature). + return (this.get('version') as U8).toNumber(); + } + + private injectSignature (signature: Signature, signer: Address, nonce: Nonce, era: ExtrinsicEra): ExtrinsicSignature { + this.set('era', era); + this.set('nonce', nonce); + this.set('signer', signer); + this.set('signature', signature); + this.set('version', new U8(BIT_VERSION | BIT_SIGNED)); + + return this; + } + + /** + * @description Adds a raw signature + */ + addSignature (_signer: Address | Uint8Array, _signature: Uint8Array, _nonce: AnyNumber, _era: Uint8Array = IMMORTAL_ERA): ExtrinsicSignature { + const signer = new Address(_signer); + const nonce = new Nonce(_nonce); + const era = new ExtrinsicEra(_era); + const signature = new Signature(_signature); + + return this.injectSignature(signature, signer, nonce, era); + } + + /** + * @description Generate a payload and pplies the signature from a keypair + */ + sign (method: Method, account: KeyringPair, { blockHash, era, nonce, version }: SignatureOptions): ExtrinsicSignature { + const signer = new Address(account.publicKey()); + const signingPayload = new SignaturePayload({ + nonce, + method, + era: era || IMMORTAL_ERA, + blockHash + }); + const signature = new Signature(signingPayload.sign(account, version as RuntimeVersion)); + + return this.injectSignature(signature, signer, signingPayload.nonce, signingPayload.era); + } + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array { + return this.isSigned + ? super.toU8a(isBare) + : new Uint8Array([this.version]); + } +} diff --git a/packages/types/src/type/Extrinsics.ts b/packages/types/src/type/Extrinsics.ts new file mode 100644 index 0000000000..18f9379e64 --- /dev/null +++ b/packages/types/src/type/Extrinsics.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Vector from '../codec/Vector'; +import Extrinsic from './Extrinsic'; + +/** + * @name Extrinsics + * @description + * A [[Vector]] of [[Extrinsic]] + */ +export default class Extrinsics extends Vector.with(Extrinsic) { +} diff --git a/packages/types/src/type/Gas.ts b/packages/types/src/type/Gas.ts new file mode 100644 index 0000000000..4b8ab772be --- /dev/null +++ b/packages/types/src/type/Gas.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U64 from '../primitive/U64'; + +/** + * @name Gas + * @description + * A gas number type for Substrate, extending [[U64]] + */ +export default class Gas extends U64 { +} diff --git a/packages/types/src/type/Hash.ts b/packages/types/src/type/Hash.ts new file mode 100644 index 0000000000..a4d98a3921 --- /dev/null +++ b/packages/types/src/type/Hash.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { IHash } from '../types'; +import H256 from '../primitive/H256'; + +/** + * @name Hash + * @description + * The default hash that is used accross the system. It is basically just a thin + * wrapper around [[H256]], representing a 32-byte blake2b (Substrate) value + */ +export default class Hash extends H256 implements IHash { +} diff --git a/packages/types/src/type/Header.spec.ts b/packages/types/src/type/Header.spec.ts new file mode 100644 index 0000000000..b36deb7284 --- /dev/null +++ b/packages/types/src/type/Header.spec.ts @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import Header from './Header'; +import json1 from '../json/Header.001.json'; +import json2 from '../json/Header.002.json'; + +describe('Header', () => { + it('decodes an actual JSON response', () => { + const header = new Header(json1.result); + + expect( + header.blockNumber.toNumber() + ).toEqual(0); + expect( + header.extrinsicsRoot.toString() + ).toEqual('0x45b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0'); + expect( + header.parentHash.toString() + ).toEqual('0x0000000000000000000000000000000000000000000000000000000000000000'); + expect( + header.stateRoot.toString() + ).toEqual('0x294c3470ae3be7555240b9d034ec19c3715ba2c3f20b92441f8cea0cab66ab56'); + expect( + header.digest.logs.toString() + ).toEqual('[]'); + }); + + it('creates a valid hash (incl. digest & compact)', () => { + const header = new Header(json2.result); + + expect( + header.hash.toHex() + ).toEqual('0x63ccfdc044d3ff4c915ad01c0d57d2ff807f4eb7d60cd41584917363bc83a99f'); + expect( + header.blockNumber.eq(new BN(2918)) + ).toBe(true); + }); +}); diff --git a/packages/types/src/type/Header.ts b/packages/types/src/type/Header.ts new file mode 100644 index 0000000000..213c9f03f3 --- /dev/null +++ b/packages/types/src/type/Header.ts @@ -0,0 +1,129 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber, AnyU8a } from '../types'; + +import { blake2AsU8a } from '@plugnet/util-crypto'; + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; + +import AccountId from './AccountId'; +import BlockNumber from './BlockNumber'; +import Digest, { DigestItem } from './Digest'; +import Hash from './Hash'; + +export type HeaderValue = { + digest?: Digest | { logs: DigestItem[] }, + extrinsicsRoot?: AnyU8a, + number?: AnyNumber, + parentHash?: AnyU8a, + stateRoot?: AnyU8a +}; + +/** + * @name Header + * @description + * A [[Block]] header + */ +export default class Header extends Struct { + constructor (value?: HeaderValue | Uint8Array | null) { + super({ + parentHash: Hash, + number: Compact.with(BlockNumber), + stateRoot: Hash, + extrinsicsRoot: Hash, + digest: Digest + }, value || {}); + } + + /** + * @description The wrapped [[BlockNumber]] + */ + get blockNumber (): BlockNumber { + return (this.get('number') as Compact).toBn() as BlockNumber; + } + + /** + * @description The wrapped [[Digest]] + */ + get digest (): Digest { + return this.get('digest') as Digest; + } + + /** + * @description The wrapped extrisics root as a [[Hash]] + */ + get extrinsicsRoot (): Hash { + return this.get('extrinsicsRoot') as Hash; + } + + /** + * @description Convenience method, encodes the header and calculates the [[Hash]] + */ + get hash (): Hash { + return new Hash( + blake2AsU8a(this.toU8a(), 256) + ); + } + + /** + * @description Alias for `blockNumber` (this is displayed in JSON) + */ + get number (): BlockNumber { + return this.blockNumber; + } + + /** + * @description The wrapped parent as a [[Hash]] + */ + get parentHash (): Hash { + return this.get('parentHash') as Hash; + } + + /** + * @description The wrapped state root as a [[Hash]] + */ + get stateRoot (): Hash { + return this.get('stateRoot') as Hash; + } +} + +/** + * @name HeaderExtended + * @description + * A [[Block]] header with an additional `author` field that indicates the block author + */ +export class HeaderExtended extends Header { + private _author?: AccountId; + + constructor (header: Header | null = null, sessionValidators: Array = []) { + super(header); + + const digestItem = header && header.digest && header.digest.logs.find(({ type }) => type === 'Seal'); + + this._author = digestItem && sessionValidators.length + ? sessionValidators[digestItem.asSeal.slot.toNumber() % sessionValidators.length] + : undefined; + } + + /** + * @description Convenience method, returns the author for the block + */ + get author (): AccountId | undefined { + return this._author; + } + + /** + * @description Creates the JSON representation + */ + toJSON (): any { + return { + ...super.toJSON(), + author: this.author + ? this.author.toJSON() + : undefined + }; + } +} diff --git a/packages/types/src/type/IndividualExposure.ts b/packages/types/src/type/IndividualExposure.ts new file mode 100644 index 0000000000..d19993ce47 --- /dev/null +++ b/packages/types/src/type/IndividualExposure.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import AccountId from './AccountId'; +import Balance from './Balance'; + +/** + * @name IndividualExposure + * @description + * The Substrate IndividualExposure for staking + */ +export default class IndividualExposure extends Struct { + constructor (value?: any) { + super({ + who: AccountId, + value: Compact.with(Balance) + }, value); + } + + /** + * @description The value + */ + get value (): Balance { + return (this.get('value') as Compact).toBn() as Balance; + } + + /** + * @description The AccountId + */ + get who (): AccountId { + return this.get('who') as AccountId; + } +} diff --git a/packages/types/src/type/InherentOfflineReport.ts b/packages/types/src/type/InherentOfflineReport.ts new file mode 100644 index 0000000000..d80c2de7b8 --- /dev/null +++ b/packages/types/src/type/InherentOfflineReport.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Tuple from '../codec/Tuple'; + +/** + * @name InherentOfflineReport + * @description + * Describes the offline-reporting extrinsic + */ +export default class InherentOfflineReport extends Tuple.with({}) { +} diff --git a/packages/types/src/type/Justification.spec.ts b/packages/types/src/type/Justification.spec.ts new file mode 100644 index 0000000000..e8414efc24 --- /dev/null +++ b/packages/types/src/type/Justification.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import rpc from '../json/SignedBlock.001.json'; +import { RhdJustification } from './Justification'; + +describe('RhdJustification', () => { + const just = new RhdJustification({ + roundNumber: 32, + hash: '0xabcd', + signatures: [ + ['0x1234', '0x5678'], + ['0x9876', '0x5432'] + ] + }); + + it('has the correct hash', () => { + expect(just.hash.toHex()).toEqual('0xabcd000000000000000000000000000000000000000000000000000000000000'); + }); + + it('has the correct round', () => { + expect(just.roundNumber.toNumber()).toEqual(32); + }); + + it('has the correct signatures', () => { + const sig = just.signatures[1]; + + expect(sig.authorityId.toHex()).toEqual('0x9876000000000000000000000000000000000000000000000000000000000000'); + }); + + it('creates from a JSON strusture', () => { + const viaRpc = new RhdJustification(rpc.result.justification); + + expect( + viaRpc.hash.toHex() + ).toEqual('0xfab4bb3a8b0a072d3d09858dc865ad2750b2e708536b6dacc89fad369eba781a'); + }); +}); diff --git a/packages/types/src/type/Justification.ts b/packages/types/src/type/Justification.ts new file mode 100644 index 0000000000..88400b4557 --- /dev/null +++ b/packages/types/src/type/Justification.ts @@ -0,0 +1,64 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber, AnyU8a } from '../types'; + +import Struct from '../codec/Struct'; +import Vector from '../codec/Vector'; +import Bytes from '../primitive/Bytes'; +import U32 from '../primitive/U32'; +import Hash from './Hash'; +import { BftAuthoritySignature, BftAuthoritySignatureValue } from './Bft'; + +export type RhdJustificationValue = { + roundNumber?: AnyNumber, + hash?: AnyU8a, + signatures?: Array +}; + +/** + * @name Justification + * @description + * A generic justification as a stream of [[Bytes]], this is specific per consensus implementation + */ +export default class Justification extends Bytes { +} + +/** + * @name RhdJustification + * @description + * [[Justification]] for the Rhododendron consensus algorithm + */ +export class RhdJustification extends Struct { + constructor (value?: RhdJustificationValue | Uint8Array) { + super({ + roundNumber: U32, + hash: Hash, + signatures: Vector.with(BftAuthoritySignature) + }, value, new Map([ + ['roundNumber', 'round_number'] + ])); + } + + /** + * @description The justification [[Hash]] + */ + get hash (): Hash { + return this.get('hash') as Hash; + } + + /** + * @description The round this justification wraps as a [[U32]] + */ + get roundNumber (): U32 { + return this.get('roundNumber') as U32; + } + + /** + * @description The [[BftAuthoritySignature]] array + */ + get signatures (): Vector { + return this.get('signatures') as Vector; + } +} diff --git a/packages/types/src/type/Key.ts b/packages/types/src/type/Key.ts new file mode 100644 index 0000000000..f6f7bbd274 --- /dev/null +++ b/packages/types/src/type/Key.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Bytes from '../primitive/Bytes'; + +/** + * @name Key + * @description + * The Substrate Key representation as a [[Bytes]] (`vec`). + */ + +export default class Key extends Bytes { +} diff --git a/packages/types/src/type/KeyValue.spec.ts b/packages/types/src/type/KeyValue.spec.ts new file mode 100644 index 0000000000..159ea7d5d9 --- /dev/null +++ b/packages/types/src/type/KeyValue.spec.ts @@ -0,0 +1,60 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import KeyValue, { KeyValueOption } from './KeyValue'; + +describe('KeyValue', () => { + it('decodes KeyValue from u8a', () => { + expect( + new KeyValue( + Uint8Array.from([ + 4 << 2, + 0x11, 0x22, 0x33, 0x44, + 9 << 2, + 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 + ]) + ).toJSON() + ).toEqual({ + key: '0x11223344', + value: '0x998877665544332211' + }); + }); + + it('encodes KeyValue from JSON', () => { + expect( + new KeyValue({ + key: '0x11223344', + value: '0x998877665544332211' + }).toU8a() + ).toEqual( + new Uint8Array([ + 4 << 2, + 0x11, 0x22, 0x33, 0x44, + 9 << 2, + 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 + ]) + ); + }); + + it('exposes the properties for key/value', () => { + const kv = new KeyValue({ + key: '0x11223344', + value: '0x998877665544332211' + }); + + expect(kv.key.toHex()).toEqual('0x11223344'); + expect(kv.value.toHex()).toEqual('0x998877665544332211'); + }); +}); + +describe('KeyValueOption', () => { + it('exposes the properties for key/value', () => { + const kv = new KeyValueOption([ + '0x11223344' + ]); + + expect(kv.key.toHex()).toEqual('0x11223344'); + expect(kv.value.isNone).toEqual(true); + }); +}); diff --git a/packages/types/src/type/KeyValue.ts b/packages/types/src/type/KeyValue.ts new file mode 100644 index 0000000000..2df44ea89c --- /dev/null +++ b/packages/types/src/type/KeyValue.ts @@ -0,0 +1,79 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyU8a } from '../types'; + +import Option from '../codec/Option'; +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import StorageData from '../primitive/StorageData'; +import StorageKey from '../primitive/StorageKey'; + +type KeyValueValue = { + key?: AnyU8a, + value?: AnyU8a +}; + +/** + * @name KeyValue + * @description + * KeyValue structure. Since most of the keys and resultant values in Subtrate is + * hashed and/or encoded, this does not wrap [[Text]], but rather a [[Bytes]] + * for the keys and values. (Not to be confused with the KeyValue in [[Metadata]], that + * is actually for Maps, whereas this is a representation of actaul storage values) + */ +export default class KeyValue extends Struct { + constructor (value?: KeyValueValue | Uint8Array) { + super({ + key: StorageKey, + value: StorageData + }, value); + } + + /** + * @description The [[StorageKey]] + */ + get key (): StorageKey { + return this.get('key') as StorageKey; + } + + /** + * @description The [[StorageData]] + */ + get value (): StorageData { + return this.get('value') as StorageData; + } +} + +export type KeyValueOptionValue = [AnyU8a, AnyU8a?]; + +/** + * @name KeyValueOption + * @description + * A key/value change. This is similar to the [[KeyValue]] structure, + * however in this case the value could be optional. Here it extends + * from a [[Tuple]], indicating the use inside areas such as [[StorageChangeSet]] + */ +export class KeyValueOption extends Tuple { + constructor (value?: KeyValueOptionValue | Uint8Array) { + super({ + StorageKey, + 'Option': Option.with(StorageData) + }, value); + } + + /** + * @description The [[StorageKey]] + */ + get key (): StorageKey { + return this[0] as StorageKey; + } + + /** + * @description The [[Option]] [[StorageData]] + */ + get value (): Option { + return this[1] as Option; + } +} diff --git a/packages/types/src/type/LockIdentifier.ts b/packages/types/src/type/LockIdentifier.ts new file mode 100644 index 0000000000..ea7bb62999 --- /dev/null +++ b/packages/types/src/type/LockIdentifier.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U8aFixed from '../codec/U8aFixed'; + +/** + * @name LockIdentifier + * @description + * The Substrate LockIdentifier for staking + */ +export default class LockIdentifier extends U8aFixed { + constructor (value?: any) { + super(value, 64); // [u8; 8] + } +} diff --git a/packages/types/src/type/LockPeriods.ts b/packages/types/src/type/LockPeriods.ts new file mode 100644 index 0000000000..3318412bcb --- /dev/null +++ b/packages/types/src/type/LockPeriods.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import I8 from '../primitive/I8'; + +/** + * @name LocKPeriods + * @description + * A number of lock periods. + */ +export default class LocKPeriods extends I8 { +} diff --git a/packages/types/src/type/MisbehaviorReport.spec.ts b/packages/types/src/type/MisbehaviorReport.spec.ts new file mode 100644 index 0000000000..832352e7a8 --- /dev/null +++ b/packages/types/src/type/MisbehaviorReport.spec.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import MisbehaviorReport, { BftAtReport } from './MisbehaviorReport'; + +describe('BftAtReport', () => { + const report = new BftAtReport({ + round: 16, + a: ['0x1234', '0x5678'], + b: ['0x8765', '0x4321'] + }); + + it('has the correct round', () => { + expect(report.round.toNumber()).toEqual(16); + }); + + it('has the correct hash (a)', () => { + expect(report.a.hash.toHex()).toEqual('0x1234000000000000000000000000000000000000000000000000000000000000'); + }); + + it('has the correct signature (b)', () => { + expect(report.b.signature.toHex()).toEqual('0x43210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'); + }); +}); + +describe('MisbehaviorReport', () => { + const report = new MisbehaviorReport({ + parentHash: '0x01020304', + parentNumber: 78, + target: '0x11112222', + misbehavior: 3 + }); + + it('has the correct misbehavior type', () => { + expect(report.misbehavior.type).toEqual('BftDoubleCommit'); + }); + + it('has the correct parent block', () => { + expect(report.parentHash.toHex()).toEqual('0x0102030400000000000000000000000000000000000000000000000000000000'); + expect(report.parentNumber.toNumber()).toEqual(78); + }); + + it('identifies the misbehaving authority', () => { + expect(report.target.toHex()).toEqual('0x1111222200000000000000000000000000000000000000000000000000000000'); + }); +}); diff --git a/packages/types/src/type/MisbehaviorReport.ts b/packages/types/src/type/MisbehaviorReport.ts new file mode 100644 index 0000000000..07100c71df --- /dev/null +++ b/packages/types/src/type/MisbehaviorReport.ts @@ -0,0 +1,209 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import EnumType from '../codec/EnumType'; +import Struct from '../codec/Struct'; +import U32 from '../primitive/U32'; +import AuthorityId from './AuthorityId'; +import { BftHashSignature, BftHashSignatureValue } from './Bft'; +import BlockNumber from './BlockNumber'; +import Hash from './Hash'; + +type BftAtReportValueSingle = { + round?: AnyNumber, + a?: BftHashSignatureValue +}; + +type BftAtReportValue = BftAtReportValueSingle & { + b?: BftHashSignatureValue +}; + +/** + * @name BftAtReport + * @description + * A report of a/b hash-signature pairs for a specific index. This is the same + * structure as is used in BftDoublePrepare & BftDoubleCommit + */ +// FIXME It is not entirely obvious from the actual Rust code what the specific +// items in the structure is called, except a & b (one should be expected, the +// other actual) +export class BftAtReport extends Struct { + constructor (value?: BftAtReportValue | Uint8Array) { + super({ + round: U32, + a: BftHashSignature, + b: BftHashSignature + }, value); + } + + /** + * @description The first report [[BftHashSignature]] + */ + get a (): BftHashSignature { + return this.get('a') as BftHashSignature; + } + + /** + * @description The second report [[BftHashSignature]] + */ + get b (): BftHashSignature { + return this.get('b') as BftHashSignature; + } + + /** + * @description The round this report applies to as [[U32]] + */ + get round (): U32 { + return this.get('round') as U32; + } +} + +/** + * @name BftProposeOutOfTurn + * @description + * A report for out-of-turn proposals + */ +export class BftProposeOutOfTurn extends Struct { + constructor (value?: BftAtReportValue | Uint8Array) { + super({ + round: U32, + a: BftHashSignature + }, value); + } + + /** + * @description The [[BftHashSignature]] the report applies to + */ + get a (): BftHashSignature { + return this.get('a') as BftHashSignature; + } + + /** + * @description The round as [[u32]] + */ + get round (): U32 { + return this.get('round') as U32; + } +} + +/** + * @name BftDoublePropose + * @description + * Report of a double-propose + */ +export class BftDoublePropose extends BftAtReport { +} + +/** + * @name BftDoublePrepare + * @description + * Report of a double-prepare + */ +export class BftDoublePrepare extends BftAtReport { +} + +/** + * @name BftDoubleCommit + * @description + * Report of a double-commit + */ +export class BftDoubleCommit extends BftAtReport { +} + +/** + * @name MisbehaviorKind + * @description + * An [[EnumType]] containing a Bft misbehaviour + */ +export class MisbehaviorKind extends EnumType { + constructor (value?: BftAtReportValue | Uint8Array, index?: number) { + super({ + BftProposeOutOfTurn, + BftDoublePropose, + BftDoublePrepare, + BftDoubleCommit + }, value, index); + } + + /** + * @description Returns the item as a [[BftDoubleCommit]] + */ + get asBftDoubleCommit (): BftDoubleCommit { + return this.value as BftDoubleCommit; + } + + /** + * @description Returns the item as a [[BftDoublePrepare]] + */ + get asBftDoublePrepare (): BftDoublePrepare { + return this.value as BftDoublePrepare; + } + + /** + * @description Returns the item as a [[BftDoublePropose]] + */ + get asBftDoublePropose (): BftDoublePropose { + return this.value as BftDoublePropose; + } + + /** + * @description Returns the item as a [[BftProposeOutOfTurn]] + */ + get asBftProposeOutOfTurn (): BftProposeOutOfTurn { + return this.value as BftProposeOutOfTurn; + } +} + +type MisbehaviorReportValue = { + misbehavior?: MisbehaviorKind | number, + parentHash?: Hash | Uint8Array | string, + parentNumber?: AnyNumber, + target?: AuthorityId | string +}; + +/** + * @name MisbehaviorReport + * @description + * A Misbehaviour report of [[MisbehavioirKind]] against a specific [[AuthorityId]] + */ +export default class MisbehaviorReport extends Struct { + constructor (value?: MisbehaviorReportValue | Uint8Array) { + super({ + parentHash: Hash, + parentNumber: BlockNumber, + target: AuthorityId, + misbehavior: MisbehaviorKind + }, value); + } + + /** + * @description The [[MisbehaviorKind]] + */ + get misbehavior (): MisbehaviorKind { + return this.get('misbehavior') as MisbehaviorKind; + } + + /** + * @description The [[Hash]] of the parent block + */ + get parentHash (): Hash { + return this.get('parentHash') as Hash; + } + + /** + * @description The [[BlockNumber]] of the parent + */ + get parentNumber (): BlockNumber { + return this.get('parentNumber') as BlockNumber; + } + + /** + * @description The [[authorityId]] the report applies to + */ + get target (): AuthorityId { + return this.get('target') as AuthorityId; + } +} diff --git a/packages/types/src/type/NewAccountOutcome.spec.ts b/packages/types/src/type/NewAccountOutcome.spec.ts new file mode 100644 index 0000000000..e8d3a8deb3 --- /dev/null +++ b/packages/types/src/type/NewAccountOutcome.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import NewAccountOutcome from './NewAccountOutcome'; + +describe('NewAccountOutcome', () => { + it('allows setting value in constructor', () => { + expect( + new NewAccountOutcome(2).toNumber() + ).toEqual(2); + }); +}); diff --git a/packages/types/src/type/NewAccountOutcome.ts b/packages/types/src/type/NewAccountOutcome.ts new file mode 100644 index 0000000000..f5400d4944 --- /dev/null +++ b/packages/types/src/type/NewAccountOutcome.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Enum from '../codec/Enum'; +import U8a from '../codec/U8a'; + +/** + * @name NewAccountOutcome + * @description + * Enum to track the outcome for creation of an [[AccountId]] + */ +export default class NewAccountOutcome extends Enum { + constructor (index?: U8a | Uint8Array | number) { + super([ + 'NoHint', + 'GoodHint', + 'BadHint' + ], index); + } +} diff --git a/packages/types/src/type/Nonce.ts b/packages/types/src/type/Nonce.ts new file mode 100644 index 0000000000..2a9de53354 --- /dev/null +++ b/packages/types/src/type/Nonce.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U64 from '../primitive/U64'; + +/** + * @name Nonce + * @description + * The Nonce or number of transactions sent by a specific account. Generally used + * with extrinsics to determine the order of execution. implemented as a Substrate + * [[U64]] + */ +export default class Nonce extends U64 { +} diff --git a/packages/types/src/type/NonceCompact.ts b/packages/types/src/type/NonceCompact.ts new file mode 100644 index 0000000000..8c841fefb8 --- /dev/null +++ b/packages/types/src/type/NonceCompact.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Nonce from './Nonce'; + +/** + * @name Nonce + * @description + * The Compact or number of transactions sent by a specific account. Generally used + * with extrinsics to determine the order of execution. + */ +export default class NonceCompact extends Compact.with(Nonce) { +} diff --git a/packages/types/src/type/Origin.spec.ts b/packages/types/src/type/Origin.spec.ts new file mode 100644 index 0000000000..52ea41f493 --- /dev/null +++ b/packages/types/src/type/Origin.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Origin from './Origin'; + +describe('Origin', () => { + it('does not allow construction', () => { + expect( + () => new Origin() + ).toThrow(/placeholder/); + }); +}); diff --git a/packages/types/src/type/Origin.ts b/packages/types/src/type/Origin.ts new file mode 100644 index 0000000000..7246b6d6f1 --- /dev/null +++ b/packages/types/src/type/Origin.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Null from '../primitive/Null'; + +/** + * @name Origin + * @description + * Where Origin occurs, it should be ignored as an internal-only value, so it should + * never actually be constructed + */ +export default class Origin extends Null { + constructor () { + super(); + + throw new Error('Origin should not be constructed, it is only a placeholder for compatibility'); + } +} diff --git a/packages/types/src/type/ParaId.ts b/packages/types/src/type/ParaId.ts new file mode 100644 index 0000000000..5bc2454320 --- /dev/null +++ b/packages/types/src/type/ParaId.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name ParaId + * @description + * Identifier for a deployed parachain implemented as a [[U32]] + */ +export default class ParaId extends U32 { +} diff --git a/packages/types/src/type/Perbill.ts b/packages/types/src/type/Perbill.ts new file mode 100644 index 0000000000..57599693e5 --- /dev/null +++ b/packages/types/src/type/Perbill.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name Perbill + * @description + * Parts per billion (see also [[Permill]]) + */ +// TODO We need to think about the toNumber() and toString() here, so we +// want to multiply by 1_000_000_000 for those purposes? +export default class Perbill extends U32 { +} diff --git a/packages/types/src/type/Permill.ts b/packages/types/src/type/Permill.ts new file mode 100644 index 0000000000..fb53f317b1 --- /dev/null +++ b/packages/types/src/type/Permill.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name Permill + * @description + * Parts per million (See also [[Perbill]]) + */ +// TODO We need to think about the toNumber() and toString() here, so we +// want to multiply by 1_000_000 for those purposes? +export default class Permill extends U32 { +} diff --git a/packages/types/src/type/PrefabWasmModule.ts b/packages/types/src/type/PrefabWasmModule.ts new file mode 100644 index 0000000000..9d6c1994e8 --- /dev/null +++ b/packages/types/src/type/PrefabWasmModule.ts @@ -0,0 +1,57 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Option from '../codec/Option'; +import Struct from '../codec/Struct'; +import Bytes from '../primitive/Bytes'; +import Null from '../primitive/Null'; +import U32 from '../primitive/U32'; + +class PrefabWasmModuleReserved extends Option { +} + +/** + * @name PrefabWasmModule + * @description Struct to encode the vesting schedule of an individual account + */ +export default class PrefabWasmModule extends Struct { + constructor (value?: any) { + super({ + scheduleVersion: Compact.with(U32), + initial: Compact.with(U32), + maximum: Compact.with(U32), + _reserved: PrefabWasmModuleReserved, + code: Bytes + }, value); + } + + /** + * @description The code as [[Bytes]] + */ + get code (): Bytes { + return this.get('code') as Bytes; + } + + /** + * @description The initial as [[Compact]] + */ + get initial (): Compact { + return this.get('initial') as Compact; + } + + /** + * @description The maximum as [[Compact]] + */ + get maximum (): Compact { + return this.get('maximum') as Compact; + } + + /** + * @description The scheduleVersion value as [[Compact]] + */ + get scheduleVersion (): Compact { + return this.get('scheduleVersion') as Compact; + } +} diff --git a/packages/types/src/type/PropIndex.ts b/packages/types/src/type/PropIndex.ts new file mode 100644 index 0000000000..70dae78a67 --- /dev/null +++ b/packages/types/src/type/PropIndex.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name PropIndex + * @description + * An increasing number that represents a specific public proposal index in the + * system, implemented as a [[U32]] + */ +export default class PropIndex extends U32 { +} diff --git a/packages/types/src/type/Proposal.ts b/packages/types/src/type/Proposal.ts new file mode 100644 index 0000000000..f5fc52b1be --- /dev/null +++ b/packages/types/src/type/Proposal.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Method from '../primitive/Method'; + +/** + * @name Proposal + * @description + * A proposal in the system. It just extends [[Method]] (Proposal = Call in Rust) + */ +export default class Proposal extends Method { +} diff --git a/packages/types/src/type/ProposalIndex.ts b/packages/types/src/type/ProposalIndex.ts new file mode 100644 index 0000000000..af6b3cb65e --- /dev/null +++ b/packages/types/src/type/ProposalIndex.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name ProposalIndex + * @description + * An increasing number that represents a specific council proposal index in + * the system, implemented as [[U32]] + */ +export default class ProposalIndex extends U32 { +} diff --git a/packages/types/src/type/ReferendumIndex.ts b/packages/types/src/type/ReferendumIndex.ts new file mode 100644 index 0000000000..264303104f --- /dev/null +++ b/packages/types/src/type/ReferendumIndex.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name ReferendumIndex + * @description + * An increasing number that represents a specific referendum in the system. It + * is unique per chain. Implemented as [[U32]] + */ +export default class ReferendumIndex extends U32 { +} diff --git a/packages/types/src/type/ReferendumInfo.ts b/packages/types/src/type/ReferendumInfo.ts new file mode 100644 index 0000000000..efffb6d7b1 --- /dev/null +++ b/packages/types/src/type/ReferendumInfo.ts @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Codec } from '../types'; + +import Struct from '../codec/Struct'; +import BlockNumber from './BlockNumber'; +import Proposal from './Proposal'; +import VoteThreshold from './VoteThreshold'; + +type ReferendumInfoValue = { + end?: BlockNumber, + proposal?: Proposal, + threshold?: VoteThreshold, + delay?: BlockNumber +}; + +/** + * @name ReferendumInfo + * @description + * Info regarding an ongoing referendum + */ +export default class ReferendumInfo extends Struct { + constructor (value?: ReferendumInfoValue | Uint8Array | Map) { + super({ + end: BlockNumber, + proposal: Proposal, + threshold: VoteThreshold, + delay: BlockNumber + }, value); + } + + /** + * @description When voting on this referendum will end + */ + get end (): BlockNumber { + return this.get('end') as BlockNumber; + } + + /** + * @description The proposal being voted on + */ + get proposal (): Proposal { + return this.get('proposal') as Proposal; + } + + /** + * @description The thresholding mechanism to determine whether it passed + */ + get threshold (): VoteThreshold { + return this.get('threshold') as VoteThreshold; + } + + /** + * @description The delay (in blocks) to wait after a successful referendum before deploying + */ + get delay (): BlockNumber { + return this.get('delay') as BlockNumber; + } +} diff --git a/packages/types/src/type/RewardDestination.ts b/packages/types/src/type/RewardDestination.ts new file mode 100644 index 0000000000..f5f1ac523c --- /dev/null +++ b/packages/types/src/type/RewardDestination.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Enum from '../codec/Enum'; + +/** + * @name RewardDestination + * @description + * A destination account for payment + */ +export default class RewardDestination extends Enum { + constructor (value?: any) { + super([ + // Pay into the stash account, increasing the amount at stake accordingly. + 'Staked', + // Pay into the stash account, not increasing the amount at stake. + 'Stash', + // Pay into the controller account. + 'Controller' + ], value); + } +} diff --git a/packages/types/src/type/Schedule.ts b/packages/types/src/type/Schedule.ts new file mode 100644 index 0000000000..b683949806 --- /dev/null +++ b/packages/types/src/type/Schedule.ts @@ -0,0 +1,91 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import U32 from '../primitive/U32'; +import Gas from './Gas'; + +/** + * @name Schedule + * @description + * Definition of the cost schedule and other parameterizations for wasm vm + */ +export default class Schedule extends Struct { + constructor (value?: any) { + super({ + version: U32, + putCodePerByteCost: Gas, + growMemCost: Gas, + regularOpCost: Gas, + returnDataPerByteCost: Gas, + sandboxDataReadCost: Gas, + sandboxDataWriteCost: Gas, + maxStackHeight: U32, + maxMemoryPages: U32 + }, value); + } + + /** + * @description Gas cost of a growing memory by single page. + */ + get growMemCost (): Gas { + return this.get('growMemCost') as Gas; + } + + /** + * @description What is the maximal memory pages amount is allowed to have for a contract. + */ + get maxMemoryPages (): U32 { + return this.get('maxMemoryPages') as U32; + } + + /** + * @description How tall the stack is allowed to grow? + */ + get maxStackHeight (): U32 { + return this.get('maxStackHeight') as U32; + } + + /** + * @description Cost of putting a byte of code into the storage. + */ + get putCodePerByteCost (): Gas { + return this.get('putCodePerByteCost') as Gas; + } + + /** + * @description Gas cost of a regular operation. + */ + get regularOpCost (): Gas { + return this.get('regularOpCost') as Gas; + } + + /** + * @description Gas cost per one byte returned. + */ + get returnDataPerByteCost (): Gas { + return this.get('returnDataPerByteCost') as Gas; + } + + /** + * @description Gas cost per one byte read from the sandbox memory. + */ + get sandboxDataReadCost (): Gas { + return this.get('sandboxDataReadCost') as Gas; + } + + /** + * @description Gas cost per one byte written to the sandbox memory. + */ + get sandboxDataWriteCost (): Gas { + return this.get('sandboxDataWriteCost') as Gas; + } + + /** + * @description Version of the schedule. + */ + get version (): U32 { + return this.get('version') as U32; + } +} diff --git a/packages/types/src/type/SeedOf.ts b/packages/types/src/type/SeedOf.ts new file mode 100644 index 0000000000..2661acf12c --- /dev/null +++ b/packages/types/src/type/SeedOf.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Hash from './Hash'; + +/** + * @name SeedOf + * @description + * The Substrate SeedOf representation as a [[Hash]]. + */ +export default class SeedOf extends Hash { +} diff --git a/packages/types/src/type/SessionKey.ts b/packages/types/src/type/SessionKey.ts new file mode 100644 index 0000000000..a8234dcf66 --- /dev/null +++ b/packages/types/src/type/SessionKey.ts @@ -0,0 +1,14 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import AuthorityId from './AuthorityId'; + +/** + * @name SessionKey + * @description + * Wrapper for a SessionKey. Same as an normal [[AuthorityId]], i.e. a wrapper + * around publicKey. + */ +export default class SessionKey extends AuthorityId { +} diff --git a/packages/types/src/type/Signature.ts b/packages/types/src/type/Signature.ts new file mode 100644 index 0000000000..7a5c560e2e --- /dev/null +++ b/packages/types/src/type/Signature.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import H512 from '../primitive/H512'; + +/** + * @name Signature + * @description + * The default signature that is used accross the system. It is currectly defined + * as a 512-bit value, represented by a [[H512]]. + */ +export default class Signature extends H512 { +} + +/** + * @name Ed25519Signature + * @description + * The default Ed25519 that is used accross the system. It is currectly defined + * as a 512-bit value, represented by a [[H512]]. + */ +export class Ed25519Signature extends Signature {} + +/** + * @name Sr25519Signature + * @description + * The default Sr25519 signature that is used accross the system. It is currectly defined + * as a 512-bit value, represented by a [[H512]]. + */ +export class Sr25519Signature extends Signature {} diff --git a/packages/types/src/type/SignaturePayload.ts b/packages/types/src/type/SignaturePayload.ts new file mode 100644 index 0000000000..92944449c8 --- /dev/null +++ b/packages/types/src/type/SignaturePayload.ts @@ -0,0 +1,106 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import { AnyNumber, AnyU8a } from '../types'; + +import { blake2AsU8a } from '@plugnet/util-crypto'; + +import Struct from '../codec/Struct'; +import ExtrinsicEra from './ExtrinsicEra'; +import Hash from './Hash'; +import Method from '../primitive/Method'; +import Nonce from './NonceCompact'; +import RuntimeVersion from '../rpc/RuntimeVersion'; + +type SignaturePayloadValue = { + nonce?: AnyNumber, + method?: Method, + era?: AnyU8a | ExtrinsicEra + blockHash?: AnyU8a +}; + +/** + * @name SignaturePayload + * @description + * A signing payload for an [[Extrinsic]]. For the final encoding, it is variable length based + * on the conetnts included + * + * 8 bytes: The Transaction Index/Nonce as provided in the transaction itself. + * 2+ bytes: The Function Descriptor as provided in the transaction itself. + * 2 bytes: The Transaction Era as provided in the transaction itself. + * 32 bytes: The hash of the authoring block implied by the Transaction Era and the current block. + */ +export default class SignaturePayload extends Struct { + protected _signature?: Uint8Array; + + constructor (value?: SignaturePayloadValue | Uint8Array) { + super({ + nonce: Nonce, + method: Method, + era: ExtrinsicEra, + blockHash: Hash + }, value); + } + + /** + * @description `true` if the payload refers to a valid signature + */ + get isSigned (): boolean { + return !!(this._signature && this._signature.length === 64); + } + + /** + * @description The block [[Hash]] the signature applies to (mortal/immortal) + */ + get blockHash (): Hash { + return this.get('blockHash') as Hash; + } + + /** + * @description The [[Method]] contained in the payload + */ + get method (): Method { + return this.get('method') as Method; + } + + /** + * @description The [[ExtrinsicEra]] + */ + get era (): ExtrinsicEra { + return this.get('era') as ExtrinsicEra; + } + + /** + * @description The [[Nonce]] + */ + get nonce (): Nonce { + return this.get('nonce') as Nonce; + } + + /** + * @description The raw signature as a `Uint8Array` + */ + get signature (): Uint8Array { + if (!this.isSigned) { + throw new Error('Transaction is not signed'); + } + + return this._signature as Uint8Array; + } + + /** + * @description Sign the payload with the keypair + */ + sign (signerPair: KeyringPair, version?: RuntimeVersion): Uint8Array { + const u8a = this.toU8a(); + const encoded = u8a.length > 256 + ? blake2AsU8a(u8a) + : u8a; + + this._signature = signerPair.sign(encoded); + + return this._signature; + } +} diff --git a/packages/types/src/type/StakingLedger.ts b/packages/types/src/type/StakingLedger.ts new file mode 100644 index 0000000000..d946821738 --- /dev/null +++ b/packages/types/src/type/StakingLedger.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import Vector from '../codec/Vector'; +import AccountId from './AccountId'; +import Balance from './Balance'; +import BlockNumber from './BlockNumber'; +import UnlockChunk from './UnlockChunk'; + +/** + * @name StakingLedger + * @description + * The ledger of a (bonded) stash + */ +export default class StakingLedger extends Struct { + constructor (value?: any) { + super({ + stash: AccountId, + total: Compact.with(Balance), + active: Compact.with(Balance), + unlocking: Vector.with(UnlockChunk) + }, value); + } + + /** + * @description The total amount of the stash's balance that will be at stake in any forthcoming rounds + */ + get active (): Balance { + return (this.get('active') as Compact).toBn() as BlockNumber; + } + + /** + * @description The stash account whose balance is actually locked and at stake + */ + get stash (): AccountId { + return this.get('stash') as AccountId; + } + + /** + * @description The total amount of the stash's balance that we are currently accounting for. It's just `active` plus all the `unlocking` balances + */ + get total (): Balance { + return (this.get('total') as Compact).toBn() as Balance; + } + + /** + * @description Any balance that is becoming free, which may eventually be transferred out of the stash (assuming it doesn't get slashed first) + */ + get unlocking (): Vector { + return this.get('unlocking') as Vector; + } +} diff --git a/packages/types/src/type/StoredPendingChange.ts b/packages/types/src/type/StoredPendingChange.ts new file mode 100644 index 0000000000..58704b86df --- /dev/null +++ b/packages/types/src/type/StoredPendingChange.ts @@ -0,0 +1,75 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber, AnyU8a } from '../types'; + +import Struct from '../codec/Struct'; +import Tuple from '../codec/Tuple'; +import Vector from '../codec/Vector'; +import U64 from '../primitive/U64'; +import BlockNumber from './BlockNumber'; +import SessionKey from './SessionKey'; + +export type NextAuthorityValue = { + index?: AnyNumber, + sessionKey?: AnyU8a +}; + +export type StoredPendingChangeValue = { + scheduledAt: AnyNumber, + delay: AnyNumber, + nextAuthorities?: Array +}; + +/** + * @name NextAuthority + * @description + * The next authority available as [[SessionKey]] + */ +export class NextAuthority extends Tuple { + constructor (value?: Uint8Array | NextAuthorityValue) { + super({ + SessionKey, + U64 + }, value); + } + + get index (): U64 { + return this[1] as U64; + } + + get sessionKey (): SessionKey { + return this[0] as SessionKey; + } +} + +/** + * @name StoredPendingChange + * @description + * Stored pending change for a Grandpa events + */ +export default class StoredPendingChange extends Struct { + constructor (value?: Uint8Array | StoredPendingChangeValue) { + super({ + scheduledAt: BlockNumber, + delay: BlockNumber, + nextAuthorities: Vector.with(NextAuthority) + }, value, new Map([ + ['scheduledAt', 'scheduled_at'], + ['nextAuthorities', 'next_authorities'] + ])); + } + + get delay (): BlockNumber { + return this.get('delay') as BlockNumber; + } + + get nextAuthorities (): Vector { + return this.get('nextAuthorities') as Vector; + } + + get scheduledAt (): BlockNumber { + return this.get('scheduledAt') as BlockNumber; + } +} diff --git a/packages/types/src/type/UnlockChunk.ts b/packages/types/src/type/UnlockChunk.ts new file mode 100644 index 0000000000..9b4ce821a7 --- /dev/null +++ b/packages/types/src/type/UnlockChunk.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import Balance from './Balance'; +import BlockNumber from './BlockNumber'; + +/** + * @name UnlockChunk + * @description + * Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked + */ +export default class UnlockChunk extends Struct { + constructor (value?: any) { + super({ + value: Compact.with(Balance), + era: Compact.with(BlockNumber) + }, value); + } + + /** + * @description Era number at which point it'll be unlocked + */ + get era (): BlockNumber { + return (this.get('era') as Compact).toBn() as BlockNumber; + } + + /** + * @description Amount of funds to be unlocked + */ + get value (): Balance { + return (this.get('value') as Compact).toBn() as Balance; + } +} diff --git a/packages/types/src/type/ValidatorPrefs.spec.ts b/packages/types/src/type/ValidatorPrefs.spec.ts new file mode 100644 index 0000000000..f8a273de3b --- /dev/null +++ b/packages/types/src/type/ValidatorPrefs.spec.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import ValidatorPrefs from './ValidatorPrefs'; + +describe('ValidatorPrefs', () => { + it('starts with empty values', () => { + expect( + new ValidatorPrefs().validatorPayment.toNumber() + ).toEqual(0); + }); + + it('exposes the internal values via get', () => { + const prefs = new ValidatorPrefs({ + unstakeThreshold: 3, + validatorPayment: 12345 + }); + + expect(prefs.unstakeThreshold.toNumber()).toEqual(3); + expect(prefs.validatorPayment.toNumber()).toEqual(12345); + }); +}); diff --git a/packages/types/src/type/ValidatorPrefs.ts b/packages/types/src/type/ValidatorPrefs.ts new file mode 100644 index 0000000000..070277bdc9 --- /dev/null +++ b/packages/types/src/type/ValidatorPrefs.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { AnyNumber } from '../types'; + +import Compact from '../codec/Compact'; +import Struct from '../codec/Struct'; +import U32 from '../primitive/U32'; +import Balance from './Balance'; + +type ValidatorPrefsValue = { + unstakeThreshold?: AnyNumber, + validatorPayment?: AnyNumber +}; + +/** + * @name ValidatorPrefs + * @description + * Validator preferences + */ +export default class ValidatorPrefs extends Struct { + constructor (value?: ValidatorPrefsValue | Uint8Array) { + super({ + unstakeThreshold: Compact.with(U32), + validatorPayment: Compact.with(Balance) + }, value); + } + + /** + * @description The unstake threshold as [[U32]] + */ + get unstakeThreshold (): U32 { + return this.get('unstakeThreshold') as U32; + } + + /** + * @description The payment config for the validator as a [[Compact]] [[Balance]] + */ + get validatorPayment (): Compact { + return this.get('validatorPayment') as Compact; + } +} diff --git a/packages/types/src/type/VestingSchedule.ts b/packages/types/src/type/VestingSchedule.ts new file mode 100644 index 0000000000..4a6800db4f --- /dev/null +++ b/packages/types/src/type/VestingSchedule.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Struct from '../codec/Struct'; +import Balance from './Balance'; + +/** + * @name VestingSchedule + * @description Struct to encode the vesting schedule of an individual account + */ +export default class VestingSchedule extends Struct { + constructor (value?: any) { + super({ + offset: Balance, + perBlock: Balance + }, value); + } + + /** + * @description The offset as [[Balance]] + */ + get offset (): Balance { + return this.get('offset') as Balance; + } + + /** + * @description The perBlock value as [[Balance]] + */ + get perBlock (): Balance { + return this.get('perBlock') as Balance; + } +} diff --git a/packages/types/src/type/Vote.ts b/packages/types/src/type/Vote.ts new file mode 100644 index 0000000000..7b83739de4 --- /dev/null +++ b/packages/types/src/type/Vote.ts @@ -0,0 +1,42 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isBoolean } from '@plugnet/util'; + +import I8 from '../primitive/I8'; + +/** + * @name Vote + * @description + * A number of lock periods, plus a vote, one way or the other. + */ +export default class Vote extends I8 { + constructor (value?: any) { + super(Vote.decodeVote(value)); + } + + private static decodeVote (value?: any): any { + if (isBoolean(value)) { + return value ? -1 : 0; + } else if (value instanceof Boolean) { + return Vote.decodeVote(value.valueOf()); + } + + return value; + } + + /** + * @description true is the wrapped value is a positive vote + */ + get isAye (): boolean { + return this.ltn(0); + } + + /** + * @description true is the wrapped value is a negative vote + */ + get isNay (): boolean { + return !this.isAye; + } +} diff --git a/packages/types/src/type/VoteIndex.ts b/packages/types/src/type/VoteIndex.ts new file mode 100644 index 0000000000..580554baf9 --- /dev/null +++ b/packages/types/src/type/VoteIndex.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import U32 from '../primitive/U32'; + +/** + * @name VoteIndex + * @description + * Voting index, implemented as a [[U32]] + */ +export default class VoteIndex extends U32 { +} diff --git a/packages/types/src/type/VoteThreshold.spec.ts b/packages/types/src/type/VoteThreshold.spec.ts new file mode 100644 index 0000000000..743bcea929 --- /dev/null +++ b/packages/types/src/type/VoteThreshold.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import VoteThreshold from './VoteThreshold'; + +describe('VoteThreshold', () => { + it('starts with default value', () => { + expect( + new VoteThreshold().toString() + ).toEqual('Super majority approval'); + }); + + it('allows setting value in constructor', () => { + expect( + new VoteThreshold(2).toNumber() + ).toEqual(2); + }); +}); diff --git a/packages/types/src/type/VoteThreshold.ts b/packages/types/src/type/VoteThreshold.ts new file mode 100644 index 0000000000..5c8324d38d --- /dev/null +++ b/packages/types/src/type/VoteThreshold.ts @@ -0,0 +1,20 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Enum from '../codec/Enum'; + +/** + * @name VoteThreshold + * @description + * Voting threshold, used inside proposals to set change the voting tally + */ +export default class VoteThreshold extends Enum { + constructor (index?: number | Uint8Array) { + super([ + 'Super majority approval', + 'Super majority rejection', + 'Simple majority' + ], index); + } +} diff --git a/packages/types/src/type/Votes.spec.ts b/packages/types/src/type/Votes.spec.ts new file mode 100644 index 0000000000..7db8ea6849 --- /dev/null +++ b/packages/types/src/type/Votes.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Vote from './Vote'; + +describe('Vote', () => { + it('constructs via boolean true', () => { + expect(new Vote(true).toNumber()).toEqual(-1); + }); + + it('constructs via boolean false', () => { + expect(new Vote(false).toNumber()).toEqual(0); + }); + + it('has isYay for positive', () => { + expect(new Vote(new Boolean(true)).isAye).toBe(true); + }); + + it('has isYay for negative', () => { + expect(new Vote(new Boolean(false)).isNay).toBe(true); + }); + + it('is positive for negative numbers', () => { + expect(new Vote(-999).isAye).toBe(true); + }); + + it('is negative for positive numbers', () => { + expect(new Vote(999).isNay).toBe(true); + }); +}); diff --git a/packages/types/src/type/WithdrawReasons.ts b/packages/types/src/type/WithdrawReasons.ts new file mode 100644 index 0000000000..5a868ba043 --- /dev/null +++ b/packages/types/src/type/WithdrawReasons.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import Set from '../codec/Set'; + +/** + * @name WithdrawReasons + * @description + * The Substrate WithdrawReasons for staking + */ +export default class WithdrawReasons extends Set { + constructor (value: any = ['header', 'body', 'justification']) { + super({ + TransactionPayment: 0b00000001, + Transfer: 0b00000010, + Reserve: 0b00000100 + }, value); + } + + /** + * @description In order to reserve some funds for a later return or repatriation + */ + get isReserve (): boolean { + return this.values.includes('Reserve'); + } + + /** + * @description In order to pay for (system) transaction costs + */ + get isTransactionPayment (): boolean { + return this.values.includes('TransactionPayment'); + } + + /** + * @description In order to transfer ownership + */ + get isTransfer (): boolean { + return this.values.includes('Transfer'); + } +} diff --git a/packages/types/src/type/index.ts b/packages/types/src/type/index.ts new file mode 100644 index 0000000000..4823e7ae60 --- /dev/null +++ b/packages/types/src/type/index.ts @@ -0,0 +1,73 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// NOTE When adding any types here, we need to update the coumentation links as +// well - /docs/SUMMARY.md as well as ../README.md + +/** + * @summary Type definitions that are used in the system + */ +export { default as AccountId, AccountIdOf } from './AccountId'; +export { default as AccountIndex } from './AccountIndex'; +export { default as AccountInfo } from './AccountInfo'; +export { default as Address } from './Address'; +export { default as Amount } from './Amount'; +export { default as AssetOf } from './AssetOf'; +export { default as AttestedCandidate } from './AttestedCandidate'; +export { default as AuthorityId } from './AuthorityId'; +export { default as Balance, BalanceOf } from './Balance'; +export { default as BalanceLock } from './BalanceLock'; +// NOTE Bft items are only used in internal structures +// export * from './Bft; +export { default as Block } from './Block'; +export { default as BlockNumber } from './BlockNumber'; +export { default as CodeHash } from './CodeHash'; +export { default as Digest, DigestItem } from './Digest'; +export { default as Event } from './Event'; +export { default as EventRecord } from './EventRecord'; +export { default as Exposure } from './Exposure'; +export { default as Extrinsic } from './Extrinsic'; +export { default as ExtrinsicEra } from './ExtrinsicEra'; +export { default as ExtrinsicSignature } from './ExtrinsicSignature'; +// NOTE Only used internally, exported as PendingExtrinsics +// export { default as Extrinsics } from './Extrinsics'; +export { default as Gas } from './Gas'; +export { default as Hash } from './Hash'; +export { default as Header, HeaderExtended } from './Header'; +export { default as IndividualExposure } from './IndividualExposure'; +export { default as InherentOfflineReport } from './InherentOfflineReport'; +export { default as Key } from './Key'; +export { default as KeyValue } from './KeyValue'; +export { default as LockIdentifier } from './LockIdentifier'; +export { default as LockPeriods } from './LockPeriods'; +export { default as MisbehaviorReport } from './MisbehaviorReport'; +export { default as NewAccountOutcome } from './NewAccountOutcome'; +// NOTE Nonce is renamed to Index +export { default as Index } from './Nonce'; +export { default as IndexCompact } from './NonceCompact'; +export { default as Justification } from './Justification'; +export { default as Origin } from './Origin'; +export { default as ParaId } from './ParaId'; +export { default as Permill } from './Permill'; +export { default as Perbill } from './Perbill'; +export { default as PrefabWasmModule } from './PrefabWasmModule'; +export { default as PropIndex } from './PropIndex'; +export { default as Proposal } from './Proposal'; +export { default as ProposalIndex } from './ProposalIndex'; +export { default as ReferendumIndex } from './ReferendumIndex'; +export { default as ReferendumInfo } from './ReferendumInfo'; +export { default as RewardDestination } from './RewardDestination'; +export { default as Schedule } from './Schedule'; +export { default as SeedOf } from './SeedOf'; +export { default as SessionKey } from './SessionKey'; +export { default as Signature, Ed25519Signature, Sr25519Signature } from './Signature'; +export { default as StakingLedger } from './StakingLedger'; +export { default as StoredPendingChange } from './StoredPendingChange'; +export { default as UnlockChunk } from './UnlockChunk'; +export { default as ValidatorPrefs } from './ValidatorPrefs'; +export { default as VestingSchedule } from './VestingSchedule'; +export { default as Vote } from './Vote'; +export { default as VoteThreshold } from './VoteThreshold'; +export { default as VoteIndex } from './VoteIndex'; +export { default as WithdrawReasons } from './WithdrawReasons'; diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts new file mode 100644 index 0000000000..f6b143f063 --- /dev/null +++ b/packages/types/src/types.ts @@ -0,0 +1,129 @@ +// Copyright 2017-2019 @polkadot/types authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeyringPair } from '@plugnet/keyring/types'; +import BN from 'bn.js'; +import U8a from './codec/U8a'; +import Method from './primitive/Method'; +import Address from './type/Address'; +import { FunctionMetadata } from './Metadata/v0/Modules'; + +export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject; + +export type CodecCallback = (result: T) => any; + +interface CodecArgObject { + [index: string]: CodecArg; +} + +interface CodecArgArray extends Array { } + +export type AnyNumber = BN | Uint8Array | number | string; + +export type AnyString = string | String; + +export type AnyU8a = Uint8Array | Array | string; + +/** + * @name Codec + * @description + * The base Codec interface. All types implement the interface provided here. Additionally + * implementors can add their own specific interfaces and helpres with getters and functions. + * The Codec Base is however required for operating as an encoding/decoding layer + */ +export interface Codec { + /** + * @description The length of the value when encoded as a Uint8Array + */ + encodedLength: number; + + /** + * @description Checks if the value is an empty value + */ + isEmpty: boolean; + + /** + * @description Compares the value of the input to see if there is a match + */ + eq (other?: any): boolean; + + /** + * @description Returns a hex string representation of the value + */ + toHex (): string; + + /** + * @description Converts the Object to JSON, typically used for RPC transfers + */ + toJSON (): any; + + /** + * @description Returns the string representation of the value + */ + toString (): string; + + /** + * @description Encodes the value as a Uint8Array as per the parity-codec specifications + * @param isBare true when the value has none of the type-specific prefixes (internal) + */ + toU8a (isBare?: boolean): Uint8Array; +} + +export type CodecTo = 'toHex' | 'toJSON' | 'toString' | 'toU8a'; + +export interface Constructor { + new(...value: Array): T; +} + +export type ConstructorDef = { [index: string]: Constructor }; + +export type TypeDef = { [index: string]: Codec }; + +export type RegistryTypes = { + [name: string]: Constructor | string | { [name: string]: string } +}; + +export interface RuntimeVersionInterface { + readonly apis: Array; + readonly authoringVersion: BN; + readonly implName: String; + readonly implVersion: BN; + readonly specName: String; + readonly specVersion: BN; +} + +export type SignatureOptions = { + blockHash: AnyU8a, + era?: Uint8Array, + nonce: AnyNumber, + version?: RuntimeVersionInterface +}; + +export interface ArgsDef { + [index: string]: Constructor; +} + +export interface IHash extends U8a {} + +export interface IMethod extends Codec { + readonly args: Array; + readonly argsDef: ArgsDef; + readonly callIndex: Uint8Array; + readonly data: Uint8Array; + readonly hasOrigin: boolean; + readonly meta: FunctionMetadata; +} + +export interface IExtrinsicSignature extends Codec { + readonly isSigned: boolean; +} + +export interface IExtrinsic extends IMethod { + hash: IHash; + isSigned: boolean; + method: Method; + signature: IExtrinsicSignature; + addSignature (signer: Address | Uint8Array, signature: Uint8Array, nonce: AnyNumber, era?: Uint8Array): IExtrinsic; + sign (account: KeyringPair, options: SignatureOptions): IExtrinsic; +} diff --git a/packages/util-crypto/LICENSE b/packages/util-crypto/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/util-crypto/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/util-crypto/README.md b/packages/util-crypto/README.md new file mode 100644 index 0000000000..90f60cc6f6 --- /dev/null +++ b/packages/util-crypto/README.md @@ -0,0 +1,21 @@ +# @polkadot/util-crypto + +Various useful cyrpto utility functions that are used across all projects in the [@polkadot](https://polkadot.js.org) namespace. It provides utility functions with additional safety checks, allowing not only for consistent coding, but also reducing the general boilerplate. + +## Usage + +Installation - + +``` +yarn add @polkadot/util-crypto +``` + +Functions can be imported as follows: + +```js +import { mnemonicGenerate } from '@polkadot/util-crypto'; +``` + +## Utilities + +[Utilities](SUMMARY.md) diff --git a/packages/util-crypto/package.json b/packages/util-crypto/package.json new file mode 100644 index 0000000000..65c157f471 --- /dev/null +++ b/packages/util-crypto/package.json @@ -0,0 +1,46 @@ +{ + "name": "@plugnet/util-crypto", + "version": "0.76.1", + "description": "A collection of useful crypto utilities for @plugnet", + "main": "index.js", + "keywords": [ + "Plugnet" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/common.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/common/issues" + }, + "homepage": "https://github.com/polkadot-js/common/tree/master/packages/util-crypto#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@plugnet/util": "^0.76.1", + "@polkadot/wasm-crypto": "^0.8.1", + "@types/bip39": "^2.4.2", + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^3.5.0", + "@types/webassembly-js-api": "^0.0.2", + "@types/xxhashjs": "^0.2.1", + "bip39": "^2.5.0", + "blakejs": "^1.1.0", + "js-sha3": "^0.8.0", + "secp256k1": "^3.6.2", + "tweetnacl": "^1.0.1", + "xxhashjs": "^0.2.2" + } +} diff --git a/packages/util-crypto/src/blake2/asHex.spec.ts b/packages/util-crypto/src/blake2/asHex.spec.ts new file mode 100644 index 0000000000..32f586ef2a --- /dev/null +++ b/packages/util-crypto/src/blake2/asHex.spec.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { blake2AsHex } from '.'; + +describe('blake2AsHex', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a 64-bit value (specified)', () => { + expect( + blake2AsHex('abc', 64) + ).toEqual('0xd8bb14d833d59559'); + }); + + it('returns a 128-bit value (as specified)', () => { + expect( + blake2AsHex('abc', 128) + ).toEqual('0xcf4ab791c62b8d2b2109c90275287816'); + }); + + it('returns a 256-bit value (default)', () => { + expect( + blake2AsHex('abc') + ).toEqual('0xbddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319'); + }); + + it('returns a 512-bit value (as specified)', () => { + expect( + blake2AsHex('abc', 512) + ).toEqual('0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923'); + }); + + it('matches with the Rust implementation', () => { + expect( + blake2AsHex( + hexToU8a('0x454545454545454545454545454545454545454545454545454545454545454501000000000000002481853da20b9f4322f34650fea5f240dcbfb266d02db94bfa0153c31f4a29dbdbf025dd4a69a6f4ee6e1577b251b655097e298b692cb34c18d3182cac3de0dc00000000'), 256 + ) + ).toEqual('0x1025e5db74fdaf4d2818822dccf0e1604ae9ccc62f26cecfde23448ff0248abf'); + }); +}); diff --git a/packages/util-crypto/src/blake2/asHex.ts b/packages/util-crypto/src/blake2/asHex.ts new file mode 100644 index 0000000000..3886999ca5 --- /dev/null +++ b/packages/util-crypto/src/blake2/asHex.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import blake2AsU8a from './asU8a'; + +/** + * @name blake2AsHex + * @summary Creates a blake2b hex from the input. + * @description + * From a `Uint8Array` input, create the blake2b and return the result as a hex string with the specified `bitLength`. + * @example + *
+ * + * ```javascript + * import { blake2AsHex } from '@plugnet/util-crypto'; + * + * blake2AsHex('abc'); // => 0xba80a53f981c4d0d + * ``` + */ +export default function blake2AsHex (data: Uint8Array | string, bitLength: number = 256): string { + return u8aToHex( + blake2AsU8a(data, bitLength) + ); +} diff --git a/packages/util-crypto/src/blake2/asU8a.spec.ts b/packages/util-crypto/src/blake2/asU8a.spec.ts new file mode 100644 index 0000000000..32035eec4b --- /dev/null +++ b/packages/util-crypto/src/blake2/asU8a.spec.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { blake2AsU8a } from '.'; + +describe('blake2AsU8a', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a 64-bit value by default', () => { + expect( + blake2AsU8a('abc') + ).toEqual( + new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25]) + ); + }); + + it('returns a 128-bit value (as specified)', () => { + expect( + blake2AsU8a('abc', 128) + ).toEqual( + new Uint8Array([207, 74, 183, 145, 198, 43, 141, 43, 33, 9, 201, 2, 117, 40, 120, 22]) + ); + }); + + it('returns a 256-bit value (as specified)', () => { + expect( + blake2AsU8a('abc', 256) + ).toEqual( + new Uint8Array([189, 221, 129, 60, 99, 66, 57, 114, 49, 113, 239, 63, 238, 152, 87, 155, 148, 150, 78, 59, 177, 203, 62, 66, 114, 98, 200, 192, 104, 213, 35, 25]) + ); + }); + + it('returns a 512-bit value (as specified)', () => { + expect( + blake2AsU8a('abc', 512) + ).toEqual( + hexToU8a('0xba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923') + ); + }); +}); diff --git a/packages/util-crypto/src/blake2/asU8a.ts b/packages/util-crypto/src/blake2/asU8a.ts new file mode 100644 index 0000000000..edb7612014 --- /dev/null +++ b/packages/util-crypto/src/blake2/asU8a.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import blakejs from 'blakejs'; +import { u8aToU8a } from '@plugnet/util'; +import { blake2b, isReady } from '@plugnet/wasm-crypto'; + +/** + * @name blake2AsU8a + * @summary Creates a blake2b u8a from the input. + * @description + * From a `Uint8Array` input, create the blake2b and return the result as a u8a with the specified `bitLength`. + * @example + *
+ * + * ```javascript + * import { blake2AsU8a } from '@plugnet/util-crypto'; + * + * blake2AsU8a('abc'); // => [0xba, 0x80, 0xa53, 0xf98, 0x1c, 0x4d, 0x0d] + * ``` + */ +export default function blake2AsU8a (data: Uint8Array | string, bitLength: number = 256, key: Uint8Array | null = null): Uint8Array { + const byteLength = Math.ceil(bitLength / 8); + + return isReady() + ? blake2b(u8aToU8a(data), u8aToU8a(key), byteLength) + : blakejs.blake2b(data, key, byteLength); +} diff --git a/packages/util-crypto/src/blake2/index.ts b/packages/util-crypto/src/blake2/index.ts new file mode 100644 index 0000000000..00c34fb9c7 --- /dev/null +++ b/packages/util-crypto/src/blake2/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Create blake2b values with specified bitlengths + */ + +export { default as blake2AsHex } from './asHex'; +export { default as blake2AsU8a } from './asU8a'; diff --git a/packages/util-crypto/src/index.ts b/packages/util-crypto/src/index.ts new file mode 100644 index 0000000000..5c7d8b6e14 --- /dev/null +++ b/packages/util-crypto/src/index.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import './polyfill'; + +import wasmCrypto from '@plugnet/wasm-crypto'; + +export * from './blake2'; +export * from './keccak'; +export * from './key'; +export * from './mnemonic'; +export * from './nacl'; +export * from './random'; +export * from './schnorrkel'; +export * from './secp256k1'; +export * from './sha512'; +export * from './xxhash'; + +export function cryptoWaitReady (): Promise { + return wasmCrypto + .waitReady() + .then(() => true) + .catch((error) => { + console.error('Unable to initialize @polkadot/util-crypto', error); + + return false; + }); +} + +// start init process immediately +cryptoWaitReady().catch(() => { + // shouldn't happen, logged above +}); diff --git a/packages/util-crypto/src/keccak/asHex.spec.ts b/packages/util-crypto/src/keccak/asHex.spec.ts new file mode 100644 index 0000000000..9f3ecb2288 --- /dev/null +++ b/packages/util-crypto/src/keccak/asHex.spec.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { waitReady } from '@plugnet/wasm-crypto'; + +import { keccakAsHex } from '.'; + +const value = 'test value'; +const result = '2d07364b5c231c56ce63d49430e085ea3033c750688ba532b24029124c26ca5e'; + +describe('keccakAsHex', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a prefixed hex representation', () => { + expect( + keccakAsHex(value) + ).toEqual(`0x${result}`); + }); +}); diff --git a/packages/util-crypto/src/keccak/asHex.ts b/packages/util-crypto/src/keccak/asHex.ts new file mode 100644 index 0000000000..0389945e5c --- /dev/null +++ b/packages/util-crypto/src/keccak/asHex.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import keccakAsU8a from './asU8a'; + +/** + * @name keccakAsHex + * @summary Creates a keccak hex string from the input. + * @description + * From either a `string` or a `Buffer` input, create the keccak and return the result as a `0x` prefixed hex string. + * @example + *
+ * + * ```javascript + * import { keccakAsHex } from '@plugnet/util-crypto'; + * + * keccakAsHex('123'); // => 0x... + * ``` + */ +export default function keccakAsHex (value: Buffer | Uint8Array | string): string { + return u8aToHex( + keccakAsU8a(value) + ); +} diff --git a/packages/util-crypto/src/keccak/asU8a.spec.ts b/packages/util-crypto/src/keccak/asU8a.spec.ts new file mode 100644 index 0000000000..90f8410309 --- /dev/null +++ b/packages/util-crypto/src/keccak/asU8a.spec.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a, stringToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { keccakAsU8a } from '.'; + +describe('keccakAsU8a', () => { + beforeEach(async () => { + await waitReady(); + }); + + const input = 'test value'; + const output = hexToU8a( + '0x2d07364b5c231c56ce63d49430e085ea3033c750688ba532b24029124c26ca5e' + ); + + it('returns an hex representation (string)', () => { + expect( + keccakAsU8a(input) + ).toEqual(output); + }); + + it('returns an hex representation (Buffer)', () => { + expect( + keccakAsU8a( + Buffer.from(input) + ) + ).toEqual(output); + }); + + it('returns an hex representation (Uint8Array)', () => { + expect( + keccakAsU8a( + stringToU8a(input) + ) + ).toEqual(output); + }); +}); diff --git a/packages/util-crypto/src/keccak/asU8a.ts b/packages/util-crypto/src/keccak/asU8a.ts new file mode 100644 index 0000000000..600dd849d4 --- /dev/null +++ b/packages/util-crypto/src/keccak/asU8a.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import jssha3 from 'js-sha3'; +import { u8aToU8a } from '@plugnet/util'; +import { isReady, keccak256 } from '@plugnet/wasm-crypto'; + +/** + * @name keccakAsU8a + * @summary Creates a keccak Uint8Array from the input. + * @description + * From either a `string` or a `Buffer` input, create the keccak and return the result as a `Uint8Array`. + * @example + *
+ * + * ```javascript + * import { keccakAsU8a } from '@plugnet/util-crypto'; + * + * keccakAsU8a('123'); // => Uint8Array + * ``` + */ +export default function keccakAsU8a (value: Buffer | Uint8Array | string): Uint8Array { + return isReady() + ? keccak256(u8aToU8a(value)) + : new Uint8Array( + jssha3.keccak256.update(value).arrayBuffer() + ); +} diff --git a/packages/util-crypto/src/keccak/index.ts b/packages/util-crypto/src/keccak/index.ts new file mode 100644 index 0000000000..33e4f25a8e --- /dev/null +++ b/packages/util-crypto/src/keccak/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Create Keccak256 values as hex & Uint8Array output + */ + +export { default as keccakAsHex } from './asHex'; +export { default as keccakAsU8a } from './asU8a'; diff --git a/packages/util-crypto/src/key/DeriveJunction.ts b/packages/util-crypto/src/key/DeriveJunction.ts new file mode 100644 index 0000000000..998fc9ede9 --- /dev/null +++ b/packages/util-crypto/src/key/DeriveJunction.ts @@ -0,0 +1,85 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { bnToHex, compactAddLength, hexToU8a, isBn, isHex, isNumber, isString, stringToU8a } from '@plugnet/util'; + +import blake2AsU8a from '../blake2/asU8a'; + +const RE_NUMBER = /^\d+$/; + +const JUNCTION_ID_LEN: number = 32; +const BN_OPTIONS = { + bitLength: 256, + isLe: true +}; + +export default class DeriveJunction { + private _chainCode: Uint8Array = new Uint8Array(32); + private _isHard: boolean = false; + + static from (value: string): DeriveJunction { + const [code, isHard] = value[0] === '/' + ? [value.substr(1), true] + : [value, false]; + const result = new DeriveJunction(); + + result.soft( + RE_NUMBER.test(code) + ? parseInt(code, 10) + : code + ); + + return isHard + ? result.harden() + : result; + } + + get chainCode (): Uint8Array { + return this._chainCode; + } + + get isHard (): boolean { + return this._isHard; + } + + get isSoft (): boolean { + return !this._isHard; + } + + hard (value: number | BN | string | Uint8Array): DeriveJunction { + return this.soft(value).harden(); + } + + harden (): DeriveJunction { + this._isHard = true; + + return this; + } + + soft (value: number | BN | string | Uint8Array): DeriveJunction { + if (isNumber(value) || isBn(value)) { + return this.soft(bnToHex(value, BN_OPTIONS)); + } else if (isString(value)) { + return isHex(value) + ? this.soft(hexToU8a(value)) + : this.soft(compactAddLength(stringToU8a(value))); + } + + if (value.length > JUNCTION_ID_LEN) { + return this.soft(blake2AsU8a(value)); + } + + this._chainCode.fill(0); + this._chainCode.set(value, 0); + + return this; + } + + soften (): DeriveJunction { + this._isHard = false; + + return this; + } +} diff --git a/packages/util-crypto/src/key/extractPath.spec.ts b/packages/util-crypto/src/key/extractPath.spec.ts new file mode 100644 index 0000000000..3ab5c187f7 --- /dev/null +++ b/packages/util-crypto/src/key/extractPath.spec.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import keyExtractPath from './extractPath'; + +describe('keyExtractPath', () => { + it('extracts properly from soft', () => { + expect( + keyExtractPath('/1') + ).toEqual({ + parts: ['/1'], + path: [ + { _isHard: false, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ] + }); + }); + + it('extracts properly from hard', () => { + expect( + keyExtractPath('//1') + ).toEqual({ + parts: ['//1'], + path: [ + { _isHard: true, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ] + }); + }); + + it('extracts properly from hard/soft', () => { + expect( + keyExtractPath('//1/2') + ).toEqual({ + parts: ['//1', '/2'], + path: [ + { _isHard: true, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, + { _isHard: false, _chainCode: Uint8Array.from([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ] + }); + }); + + it('does not extract from invalid paths', () => { + expect( + () => keyExtractPath('1/2') + ).toThrow(/does not match input/); + }); + + it('does not extract from invalid paths', () => { + expect( + () => keyExtractPath('hello') + ).toThrow(/does not match input/); + }); +}); diff --git a/packages/util-crypto/src/key/extractPath.ts b/packages/util-crypto/src/key/extractPath.ts new file mode 100644 index 0000000000..f2578ffbce --- /dev/null +++ b/packages/util-crypto/src/key/extractPath.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import DeriveJunction from './DeriveJunction'; +import { assert } from '@plugnet/util'; + +const RE_JUNCTION = /\/(\/?)([^\/]+)/g; + +type ExtractResult = { + parts: null | Array, + path: Array +}; + +/** + * @description Extract derivation juntions from the supplied path + */ +export default function keyExtractPath (derivePath: string): ExtractResult { + const parts = derivePath.match(RE_JUNCTION); + const path: Array = []; + let constructed = ''; + + if (parts) { + constructed = parts.join(''); + + parts.forEach((value: string) => { + path.push(DeriveJunction.from(value.substr(1))); + }); + } + + assert(constructed === derivePath, `Re-constructed path "${constructed}" does not match input`); + + return { + parts, + path + }; +} diff --git a/packages/util-crypto/src/key/extractSuri.spec.ts b/packages/util-crypto/src/key/extractSuri.spec.ts new file mode 100644 index 0000000000..509f059dd1 --- /dev/null +++ b/packages/util-crypto/src/key/extractSuri.spec.ts @@ -0,0 +1,137 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import keyExtractSuri from './extractSuri'; + +describe('keyExtractSuri', () => { + it('does not extract from invalid suri', () => { + expect( + () => keyExtractSuri('//2') + ).toThrow(/to a secret URI/); + }); + + it('derives on "hello world"', () => { + expect( + keyExtractSuri('hello world') + ).toEqual({ + path: [], + phrase: 'hello world' + }); + }); + + it('derives on "hello world/1', () => { + expect( + keyExtractSuri('hello world/1') + ).toEqual({ + path: [ + { _isHard: false, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world/DOT', () => { + expect( + keyExtractSuri('hello world/DOT') + ).toEqual({ + path: [ + { _isHard: false, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world//1', () => { + expect( + keyExtractSuri('hello world//1') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world//DOT', () => { + expect( + keyExtractSuri('hello world//DOT') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world//1/DOT', () => { + expect( + keyExtractSuri('hello world//1/DOT') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, + { _isHard: false, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world//DOT/1', () => { + expect( + keyExtractSuri('hello world//DOT/1') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, + { _isHard: false, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'hello world' + }); + }); + + it('derives on "hello world///password"', () => { + expect( + keyExtractSuri('hello world///password') + ).toEqual({ + password: 'password', + path: [], + phrase: 'hello world' + }); + }); + + it('derives on "hello world//1/DOT///password"', () => { + expect( + keyExtractSuri('hello world//1/DOT///password') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, + { _isHard: false, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + password: 'password', + phrase: 'hello world' + }); + }); + + it('derives on "hello world/1//DOT///password"', () => { + expect( + keyExtractSuri('hello world/1//DOT///password') + ).toEqual({ + path: [ + { _isHard: false, _chainCode: Uint8Array.from([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) }, + { _isHard: true, _chainCode: Uint8Array.from([12, 68, 79, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + password: 'password', + phrase: 'hello world' + }); + }); + + it('derives on actual Alice', () => { + expect( + keyExtractSuri('bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice') + ).toEqual({ + path: [ + { _isHard: true, _chainCode: Uint8Array.from([20, 65, 108, 105, 99, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } + ], + phrase: 'bottom drive obey lake curtain smoke basket hold race lonely fit walk' + }); + }); +}); diff --git a/packages/util-crypto/src/key/extractSuri.ts b/packages/util-crypto/src/key/extractSuri.ts new file mode 100644 index 0000000000..2f9279cb77 --- /dev/null +++ b/packages/util-crypto/src/key/extractSuri.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert, isNull } from '@plugnet/util'; + +import DeriveJunction from './DeriveJunction'; +import keyExtractPath from './extractPath'; + +type ExtractResult = { + password?: string, + path: Array, + phrase: string +}; + +const RE_CAPTURE = /^(\w+( \w+)*)((\/\/?[^\/]+)*)(\/\/\/(.*))?$/; + +/** + * @description Extracts the phrase, path and password from a SURI format for specifying secret keys `//////` (the `///password` may be omitted, and `/` and `//` maybe repeated and mixed). + */ +export default function keyExtract (suri: string): ExtractResult { + const matches = suri.match(RE_CAPTURE); + + assert(!isNull(matches), `Unable to match '${suri}' to a secret URI`); + + const [, phrase, , derivePath, , , password] = matches as Array; + const { path } = keyExtractPath(derivePath); + + return { + password, + path, + phrase + }; +} diff --git a/packages/util-crypto/src/key/fromPath.ts b/packages/util-crypto/src/key/fromPath.ts new file mode 100644 index 0000000000..5aca2ba49e --- /dev/null +++ b/packages/util-crypto/src/key/fromPath.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { KeypairType, Keypair } from '../types'; + +import DeriveJunction from './DeriveJunction'; +import keyHdkdEd15519 from './hdkdEd25519'; +import keyHdkdSr15519 from './hdkdSr25519'; + +export default function keyFromPath (pair: Keypair, path: Array, type: KeypairType): Keypair { + const isEd25519 = type === 'ed25519'; + + return path.reduce((pair, junction) => { + return isEd25519 + ? keyHdkdEd15519(pair, junction) + : keyHdkdSr15519(pair, junction); + }, pair); +} diff --git a/packages/util-crypto/src/key/hdkdEd25519.ts b/packages/util-crypto/src/key/hdkdEd25519.ts new file mode 100644 index 0000000000..f010ed4995 --- /dev/null +++ b/packages/util-crypto/src/key/hdkdEd25519.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import { assert } from '@plugnet/util'; + +import naclDeriveHard from '../nacl/deriveHard'; +import naclKeypairFromSeed from '../nacl/keypair/fromSeed'; +import DeriveJunction from './DeriveJunction'; + +export default function keyHdkdEd25519 (keypair: Keypair, { chainCode, isHard }: DeriveJunction): Keypair { + assert(isHard, 'A soft key was found in the path (and is unsupported)'); + + return naclKeypairFromSeed( + naclDeriveHard(keypair.secretKey.subarray(0, 32), chainCode) + ); +} diff --git a/packages/util-crypto/src/key/hdkdSr25519.ts b/packages/util-crypto/src/key/hdkdSr25519.ts new file mode 100644 index 0000000000..59fc664f79 --- /dev/null +++ b/packages/util-crypto/src/key/hdkdSr25519.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import schnorrkelDeriveHard from '../schnorrkel/deriveHard'; +import schnorrkelDeriveSoft from '../schnorrkel/deriveSoft'; +import DeriveJunction from './DeriveJunction'; + +export default function keyHdkdSr25519 (keypair: Keypair, { chainCode, isSoft }: DeriveJunction): Keypair { + return isSoft + ? schnorrkelDeriveSoft(keypair, chainCode) + : schnorrkelDeriveHard(keypair, chainCode); +} diff --git a/packages/util-crypto/src/key/index.ts b/packages/util-crypto/src/key/index.ts new file mode 100644 index 0000000000..9dd50ec3dc --- /dev/null +++ b/packages/util-crypto/src/key/index.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Create keys from paths, seeds and password + */ +export { default as keyExtractPath } from './extractPath'; +export { default as keyExtractSuri } from './extractSuri'; +export { default as keyFromPath } from './fromPath'; +export { default as keyHdkdEd25519 } from './hdkdEd25519'; +export { default as keyHdkdSr25519 } from './hdkdEd25519'; diff --git a/packages/util-crypto/src/mnemonic/generate.spec.ts b/packages/util-crypto/src/mnemonic/generate.spec.ts new file mode 100644 index 0000000000..2d95068709 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/generate.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import generate from './generate'; +import validate from './validate'; +import { cryptoWaitReady } from '..'; + +describe('mnemonicGenerate', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + it('generates a valid mnemonic (default strength)', () => { + const mnemonic = generate(); + + console.error(mnemonic); + + expect( + validate(mnemonic) + ).toEqual(true); + }); + + [12, 15, 18, 21, 24].forEach((num) => { + it(`generates a valid mnemonic (${num} words)`, () => { + const mnemonic = generate(num as any); + const isValid = validate(mnemonic); + + console.error(mnemonic); + + expect(mnemonic.split(' ')).toHaveLength(num); + expect(isValid).toEqual(true); + }); + }); +}); diff --git a/packages/util-crypto/src/mnemonic/generate.ts b/packages/util-crypto/src/mnemonic/generate.ts new file mode 100644 index 0000000000..f587994101 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/generate.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +import { generateMnemonic } from 'bip39'; +import { bip39Generate, isReady } from '@plugnet/wasm-crypto'; + +type WordCount = 12 | 15 | 18 | 21 | 24; + +// mapping of words to the actual strength (as expected) +const STRENGTH_MAP = { + 12: 16 * 8, + 15: 20 * 8, + 18: 24 * 8, + 21: 28 * 8, + 24: 32 * 8 +}; + +/** + * @name mnemonicGenerate + * @summary Creates a valid mnemonic string using using [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki). + * @example + *
+ * + * ```javascript + * import { mnemonicGenerate } from '@plugnet/util-crypto'; + * + * const mnemonic = mnemonicGenerate(); // => string + * ``` + */ +export default function mnemonicGenerate (numWords: WordCount = 12): string { + return isReady() + ? bip39Generate(numWords) + : generateMnemonic(STRENGTH_MAP[numWords]); +} diff --git a/packages/util-crypto/src/mnemonic/index.ts b/packages/util-crypto/src/mnemonic/index.ts new file mode 100644 index 0000000000..331a8b06e1 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/index.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +/** + * @summary Create valid mnemonic strings, validate them using BIP39, and convert them to valid seeds + */ + +export { default as mnemonicGenerate } from './generate'; +export { default as mnemonicToEntropy } from './toEntropy'; +export { default as mnemonicToMiniSecret } from './toMiniSecret'; +export { default as mnemonicToSeed } from './toSeed'; +export { default as mnemonicValidate } from './validate'; diff --git a/packages/util-crypto/src/mnemonic/toEntropy.spec.ts b/packages/util-crypto/src/mnemonic/toEntropy.spec.ts new file mode 100644 index 0000000000..37b5c21412 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toEntropy.spec.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import toEntropy from './toEntropy'; +import tests from '../schnorrkel/keypair/testing'; +import { cryptoWaitReady } from '..'; + +describe('mnemonicToEntropy', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + tests.forEach(([mnemonic, entropy], index) => { + it(`Created correct entropy for ${index}`, () => { + expect(u8aToHex(toEntropy(mnemonic))).toEqual(entropy); + }); + }); +}); diff --git a/packages/util-crypto/src/mnemonic/toEntropy.ts b/packages/util-crypto/src/mnemonic/toEntropy.ts new file mode 100644 index 0000000000..2b337a356e --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toEntropy.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +import { mnemonicToEntropy } from 'bip39'; +import { hexAddPrefix, hexToU8a } from '@plugnet/util'; +import { bip39ToEntropy, isReady } from '@plugnet/wasm-crypto'; + +export default function toEntropy (mnemonic: string): Uint8Array { + return isReady() + ? bip39ToEntropy(mnemonic) + : hexToU8a( + hexAddPrefix( + mnemonicToEntropy(mnemonic) + ) + ); +} diff --git a/packages/util-crypto/src/mnemonic/toMiniSecret.spec.ts b/packages/util-crypto/src/mnemonic/toMiniSecret.spec.ts new file mode 100644 index 0000000000..dc7d433560 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toMiniSecret.spec.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import mnemonicToMiniSecret from './toMiniSecret'; +import tests from '../schnorrkel/keypair/testing'; +import { cryptoWaitReady } from '..'; + +describe('mnemonicToMiniSecret', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + it('generates a valid seed', () => { + expect( + u8aToHex(mnemonicToMiniSecret('seed sock milk update focus rotate barely fade car face mechanic mercy')) + ).toEqual('0x4d1ab2a57929edfd018aaa974e62ed557e3f54b4104acabedf73c8f5a1dbb029'); + }); + + tests.forEach(([mnemonic, , seed], index) => { + it(`Created correct seed for ${index}`, () => { + expect( + u8aToHex(mnemonicToMiniSecret(mnemonic, 'Substrate')) + ).toEqual( + // mini returned here, only check first 32-bytes (64 hex + 2 prefix) + seed.substr(0, 66) + ); + }); + }); +}); diff --git a/packages/util-crypto/src/mnemonic/toMiniSecret.ts b/packages/util-crypto/src/mnemonic/toMiniSecret.ts new file mode 100644 index 0000000000..8ed0626418 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toMiniSecret.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +import { pbkdf2Sync } from 'pbkdf2'; +import { bufferToU8a, stringToU8a, u8aToBuffer } from '@plugnet/util'; +import { bip39ToMiniSecret, isReady } from '@plugnet/wasm-crypto'; + +import toEntropy from './toEntropy'; + +export default function toMiniSecret (mnemonic: string, password: string = ''): Uint8Array { + if (isReady()) { + return bip39ToMiniSecret(mnemonic, password); + } + + const entropy = u8aToBuffer(toEntropy(mnemonic)); + const salt = u8aToBuffer(stringToU8a(`mnemonic${password}`)); + + // return the first 32 bytes as the seed + return bufferToU8a( + pbkdf2Sync(entropy, salt, 2048, 64, 'sha512') + ).slice(0, 32); +} diff --git a/packages/util-crypto/src/mnemonic/toSeed.spec.ts b/packages/util-crypto/src/mnemonic/toSeed.spec.ts new file mode 100644 index 0000000000..a0f48503a9 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toSeed.spec.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import toSeed from './toSeed'; + +describe('mnemonicToSeed', () => { + it('generates a valid seed', () => { + expect( + u8aToHex(toSeed('seed sock milk update focus rotate barely fade car face mechanic mercy')) + ).toEqual('0x3c121e20de068083b49c2315697fb59a2d9e8643c24e5ea7628132c58969a027'); + }); +}); diff --git a/packages/util-crypto/src/mnemonic/toSeed.ts b/packages/util-crypto/src/mnemonic/toSeed.ts new file mode 100644 index 0000000000..b581b06f09 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/toSeed.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { mnemonicToSeed } from 'bip39'; +import { bufferToU8a } from '@plugnet/util'; +import { bip39ToSeed, isReady } from '@plugnet/wasm-crypto'; + +/** + * @name toSeed + * @summary Creates a valid seed from a mnemonic input + * @example + *
+ * + * ```javascript + * import { mnemonicGenerate, mnemonicToSeed, mnemonicValidate } from '@plugnet/util-crypto'; + * + * const mnemonic = mnemonicGenerate(); // => string + * const isValidMnemonic = mnemonicValidate(mnemonic); // => boolean + * + * if (isValidMnemonic) { + * console.log(`Seed generated from mnemonic: ${mnemonicToSeed(mnemonic)}`); => u8a + * } + * ``` + */ +export default function toSeed (mnemonic: string, password: string = ''): Uint8Array { + return isReady() + ? bip39ToSeed(mnemonic, password) + : bufferToU8a(mnemonicToSeed(mnemonic, password)).subarray(0, 32); +} diff --git a/packages/util-crypto/src/mnemonic/validate.spec.ts b/packages/util-crypto/src/mnemonic/validate.spec.ts new file mode 100644 index 0000000000..b9f014b3b0 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/validate.spec.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import validate from './validate'; +import { cryptoWaitReady } from '..'; + +describe('mnemonicValidate', () => { + beforeEach(async () => { + await cryptoWaitReady(); + }); + + it('returns true on valid', () => { + expect( + validate('seed sock milk update focus rotate barely fade car face mechanic mercy') + ).toEqual(true); + }); + + it('returns false on invalid', () => { + expect( + validate('wine photo extra cushion basket dwarf humor cloud truck job boat submit') + ).toEqual(false); + }); +}); diff --git a/packages/util-crypto/src/mnemonic/validate.ts b/packages/util-crypto/src/mnemonic/validate.ts new file mode 100644 index 0000000000..835acd3776 --- /dev/null +++ b/packages/util-crypto/src/mnemonic/validate.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +import { validateMnemonic } from 'bip39'; +import { bip39Validate, isReady } from '@plugnet/wasm-crypto'; + +/** + * @name mnemonicValidate + * @summary Validates a mnemonic input using [BIP39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki). + * @example + *
+ * + * ```javascript + * import { mnemonicGenerate, mnemonicValidate } from '@plugnet/util-crypto'; + * + * const mnemonic = mnemonicGenerate(); // => string + * const isValidMnemonic = mnemonicValidate(mnemonic); // => boolean + * ``` + */ +export default function mnemonicValidate (mnemonic: string): boolean { + return isReady() + ? bip39Validate(mnemonic) + : validateMnemonic(mnemonic); +} diff --git a/packages/util-crypto/src/nacl/decrypt.spec.ts b/packages/util-crypto/src/nacl/decrypt.spec.ts new file mode 100644 index 0000000000..9289babcf0 --- /dev/null +++ b/packages/util-crypto/src/nacl/decrypt.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { naclDecrypt, naclEncrypt } from '.'; + +describe('naclDecrypt', () => { + it('decrypts a encrypted message', () => { + const secret = new Uint8Array(32); + const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]); + const { encrypted, nonce } = naclEncrypt(message, secret); + + expect( + naclDecrypt(encrypted, nonce, secret) + ).toEqual( + message + ); + }); + + it('returns null on invalid', () => { + expect( + naclDecrypt(new Uint8Array(), new Uint8Array(24), new Uint8Array(32)) + ).toEqual(null); + }); +}); diff --git a/packages/util-crypto/src/nacl/decrypt.ts b/packages/util-crypto/src/nacl/decrypt.ts new file mode 100644 index 0000000000..7cb963ffde --- /dev/null +++ b/packages/util-crypto/src/nacl/decrypt.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nacl from 'tweetnacl'; + +/** + * @name naclDecrypt + * @summary Decrypts a message using the supplied secretKey and nonce + * @description + * Returns an decrypted message, using the `secret` and `nonce`. + * @example + *
+ * + * ```javascript + * import { naclDecrypt } from '@plugnet/util-crypto'; + * + * naclDecrypt([...], [...], [...]); // => [...] + * ``` + */ +export default function naclDecrypt (encrypted: Uint8Array, nonce: Uint8Array, secret: Uint8Array): Uint8Array | null { + return nacl.secretbox.open(encrypted, nonce, secret) || null; +} diff --git a/packages/util-crypto/src/nacl/deriveHard.ts b/packages/util-crypto/src/nacl/deriveHard.ts new file mode 100644 index 0000000000..164319d567 --- /dev/null +++ b/packages/util-crypto/src/nacl/deriveHard.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { compactAddLength, stringToU8a, u8aConcat } from '@plugnet/util'; + +import blake2AsU8a from '../blake2/asU8a'; + +const HDKD = compactAddLength(stringToU8a('Ed25519HDKD')); + +export default function deriveHard (seed: Uint8Array, chainCode: Uint8Array): Uint8Array { + return blake2AsU8a( + u8aConcat(HDKD, seed, chainCode) + ); +} diff --git a/packages/util-crypto/src/nacl/encrypt.spec.ts b/packages/util-crypto/src/nacl/encrypt.spec.ts new file mode 100644 index 0000000000..2a4b4715fd --- /dev/null +++ b/packages/util-crypto/src/nacl/encrypt.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { naclEncrypt } from '.'; + +describe('naclEncrypt', () => { + it('encrypts a message', () => { + const secret = new Uint8Array(32); + const message = new Uint8Array([1, 2, 3, 4, 5, 4, 3, 2, 1]); + + expect( + naclEncrypt(message, secret, new Uint8Array(24)) + ).toEqual({ + encrypted: new Uint8Array([94, 21, 20, 69, 68, 221, 140, 245, 200, 67, 77, 188, 129, 85, 227, 141, 199, 60, 184, 251, 251, 129, 205, 46, 234]), + nonce: new Uint8Array(24) + }); + }); +}); diff --git a/packages/util-crypto/src/nacl/encrypt.ts b/packages/util-crypto/src/nacl/encrypt.ts new file mode 100644 index 0000000000..2573de1d1c --- /dev/null +++ b/packages/util-crypto/src/nacl/encrypt.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nacl from 'tweetnacl'; + +import randomAsU8a from '../random/asU8a'; + +type Encrypted = { + encrypted: Uint8Array, + nonce: Uint8Array +}; + +/** + * @name naclEncrypt + * @summary Encrypts a message using the supplied secretKey and nonce + * @description + * Returns an encrypted message, using the `secretKey` and `nonce`. If the `nonce` was not supplied, a random value is generated. + * @example + *
+ * + * ```javascript + * import { naclEncrypt } from '@plugnet/util-crypto'; + * + * naclEncrypt([...], [...]); // => [...] + * ``` + */ +export default function naclEncrypt (message: Uint8Array, secret: Uint8Array, nonce: Uint8Array = randomAsU8a(24)): Encrypted { + return { + encrypted: nacl.secretbox(message, nonce, secret), + nonce + }; +} diff --git a/packages/util-crypto/src/nacl/index.ts b/packages/util-crypto/src/nacl/index.ts new file mode 100644 index 0000000000..e246737d34 --- /dev/null +++ b/packages/util-crypto/src/nacl/index.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Implements [NaCl](http://nacl.cr.yp.to/) secret-key authenticated encryption, public-key authenticated encryption, hashing, and public-key signatures + */ +export { default as naclDecrypt } from './decrypt'; +export { default as deriveHard } from './deriveHard'; +export { default as naclEncrypt } from './encrypt'; +export { default as naclKeypairFromRandom } from './keypair/fromRandom'; +export { default as naclKeypairFromSecret } from './keypair/fromSecret'; +export { default as naclKeypairFromSeed } from './keypair/fromSeed'; +export { default as naclKeypairFromString } from './keypair/fromString'; +export { default as naclSign } from './sign'; +export { default as naclVerify } from './verify'; diff --git a/packages/util-crypto/src/nacl/keypair/fromRandom.spec.ts b/packages/util-crypto/src/nacl/keypair/fromRandom.spec.ts new file mode 100644 index 0000000000..ee020d6b3c --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromRandom.spec.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import { naclKeypairFromRandom } from '..'; + +describe('naclKeypairFromRandom', () => { + let keypair: Keypair; + + beforeEach(() => { + keypair = naclKeypairFromRandom(); + }); + + it('generates a valid publicKey', () => { + expect( + keypair.publicKey + ).toHaveLength(32); + }); + + it('generates a valid secretKey', () => { + expect( + keypair.secretKey + ).toHaveLength(64); + }); +}); diff --git a/packages/util-crypto/src/nacl/keypair/fromRandom.ts b/packages/util-crypto/src/nacl/keypair/fromRandom.ts new file mode 100644 index 0000000000..d2b680a9a3 --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromRandom.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import nacl from 'tweetnacl'; + +/** + * @name naclKeypairFromRandom + * @summary Creates a new public/secret keypair. + * @description + * Returns a new generate object containing a `publicKey` & `secretKey`. + * @example + *
+ * + * ```javascript + * import { naclKeypairFromRandom } from '@plugnet/util-crypto'; + * + * naclKeypairFromRandom(); // => { secretKey: [...], publicKey: [...] } + * ``` + */ +export default function naclKeypairFromRandom (): Keypair { + return nacl.sign.keyPair(); +} diff --git a/packages/util-crypto/src/nacl/keypair/fromSecret.spec.ts b/packages/util-crypto/src/nacl/keypair/fromSecret.spec.ts new file mode 100644 index 0000000000..d2f280fc04 --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromSecret.spec.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { naclKeypairFromSecret } from '..'; + +describe('naclKeypairFromSecret', () => { + const secretKey = new Uint8Array([ + 18, 52, 86, 120, 144, 18, 52, 86, + 120, 144, 18, 52, 86, 120, 144, 18, + 18, 52, 86, 120, 144, 18, 52, 86, + 120, 144, 18, 52, 86, 120, 144, 18, + 180, 114, 93, 155, 165, 255, 217, 82, + 16, 250, 209, 11, 193, 10, 88, 218, + 190, 190, 41, 193, 236, 252, 1, 152, + 216, 214, 0, 41, 45, 138, 13, 53 + ]); + + it('generates a valid publicKey/secretKey pair', () => { + expect( + naclKeypairFromSecret(secretKey) + ).toEqual({ + publicKey: new Uint8Array([ + 180, 114, 93, 155, 165, 255, 217, 82, + 16, 250, 209, 11, 193, 10, 88, 218, + 190, 190, 41, 193, 236, 252, 1, 152, + 216, 214, 0, 41, 45, 138, 13, 53 + ]), + secretKey + }); + }); +}); diff --git a/packages/util-crypto/src/nacl/keypair/fromSecret.ts b/packages/util-crypto/src/nacl/keypair/fromSecret.ts new file mode 100644 index 0000000000..a410f50c2d --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromSecret.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import nacl from 'tweetnacl'; + +/** + * @name naclKeypairFromSecret + * @summary Creates a new public/secret keypair from a secret. + * @description + * Returns a object containing a `publicKey` & `secretKey` generated from the supplied secret. + * @example + *
+ * + * ```javascript + * import { naclKeypairFromSecret } from '@plugnet/util-crypto'; + * + * naclKeypairFromSecret(...); // => { secretKey: [...], publicKey: [...] } + * ``` + */ +export default function naclKeypairFromSecret (secret: Uint8Array): Keypair { + return nacl.sign.keyPair.fromSecretKey(secret); +} diff --git a/packages/util-crypto/src/nacl/keypair/fromSeed.spec.ts b/packages/util-crypto/src/nacl/keypair/fromSeed.spec.ts new file mode 100644 index 0000000000..46969d9ffe --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromSeed.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { naclKeypairFromSeed } from '..'; + +describe('naclKeypairFromSeed', () => { + // NOTE: Aligned with Rust test, b"12345678901234567890123456789012" + const TEST = stringToU8a('12345678901234567890123456789012'); + const RESULT = { + publicKey: new Uint8Array([ + 0x2f, 0x8c, 0x61, 0x29, 0xd8, 0x16, 0xcf, 0x51, + 0xc3, 0x74, 0xbc, 0x7f, 0x08, 0xc3, 0xe6, 0x3e, + 0xd1, 0x56, 0xcf, 0x78, 0xae, 0xfb, 0x4a, 0x65, + 0x50, 0xd9, 0x7b, 0x87, 0x99, 0x79, 0x77, 0xee + ]), + secretKey: new Uint8Array([ + 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 49, 50, + // public part + 0x2f, 0x8c, 0x61, 0x29, 0xd8, 0x16, 0xcf, 0x51, + 0xc3, 0x74, 0xbc, 0x7f, 0x08, 0xc3, 0xe6, 0x3e, + 0xd1, 0x56, 0xcf, 0x78, 0xae, 0xfb, 0x4a, 0x65, + 0x50, 0xd9, 0x7b, 0x87, 0x99, 0x79, 0x77, 0xee + ]) + }; + + beforeEach(async () => { + await waitReady(); + }); + + it('generates a valid publicKey/secretKey pair (u8a)', () => { + expect( + naclKeypairFromSeed(TEST) + ).toEqual(RESULT); + }); +}); diff --git a/packages/util-crypto/src/nacl/keypair/fromSeed.ts b/packages/util-crypto/src/nacl/keypair/fromSeed.ts new file mode 100644 index 0000000000..50be7c903e --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromSeed.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import nacl from 'tweetnacl'; +import { isReady, ed25519KeypairFromSeed } from '@plugnet/wasm-crypto'; + +/** + * @name naclKeypairFromSeed + * @summary Creates a new public/secret keypair from a seed. + * @description + * Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed. + * @example + *
+ * + * ```javascript + * import { naclKeypairFromSeed } from '@plugnet/util-crypto'; + * + * naclKeypairFromSeed(...); // => { secretKey: [...], publicKey: [...] } + * ``` + */ +export default function naclKeypairFromSeed (seed: Uint8Array): Keypair { + if (isReady()) { + const full = ed25519KeypairFromSeed(seed); + + return { + publicKey: full.slice(32), + secretKey: full.slice(0, 64) + }; + } + + return nacl.sign.keyPair.fromSeed(seed); +} diff --git a/packages/util-crypto/src/nacl/keypair/fromString.spec.ts b/packages/util-crypto/src/nacl/keypair/fromString.spec.ts new file mode 100644 index 0000000000..a1a2c3e0b8 --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromString.spec.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { naclKeypairFromString } from '..'; + +describe('naclKeypairFromSeed', () => { + it('generates a valid publicKey/secretKey pair', () => { + expect( + naclKeypairFromString('test') + ).toEqual({ + publicKey: new Uint8Array([188, 108, 179, 142, 36, 142, 76, 87, 77, 193, 147, 139, 254, 110, 196, 217, 117, 233, 167, 165, 250, 150, 247, 237, 198, 68, 129, 4, 211, 209, 136, 48]), + secretKey: new Uint8Array([146, 139, 32, 54, 105, 67, 226, 175, 209, 30, 188, 14, 174, 46, 83, 169, 59, 241, 119, 164, 252, 243, 91, 204, 100, 213, 3, 112, 78, 101, 226, 2, 188, 108, 179, 142, 36, 142, 76, 87, 77, 193, 147, 139, 254, 110, 196, 217, 117, 233, 167, 165, 250, 150, 247, 237, 198, 68, 129, 4, 211, 209, 136, 48]) + }); + }); +}); diff --git a/packages/util-crypto/src/nacl/keypair/fromString.ts b/packages/util-crypto/src/nacl/keypair/fromString.ts new file mode 100644 index 0000000000..4165aa6562 --- /dev/null +++ b/packages/util-crypto/src/nacl/keypair/fromString.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import { stringToU8a } from '@plugnet/util'; + +import blake2AsU8a from '../../blake2/asU8a'; +import fromSeed from './fromSeed'; + +/** + * @name naclKeypairFromString + * @summary Creates a new public/secret keypair from a string. + * @description + * Returns a object containing a `publicKey` & `secretKey` generated from the supplied string. The string is hashed and the value used as the input seed. + * @example + *
+ * + * ```javascript + * import { naclKeypairFromString } from '@plugnet/util-crypto'; + * + * naclKeypairFromString('test'); // => { secretKey: [...], publicKey: [...] } + * ``` + */ +export default function naclKeypairFromString (value: string): Keypair { + return fromSeed( + blake2AsU8a( + stringToU8a(value), + 256 + ) + ); +} diff --git a/packages/util-crypto/src/nacl/sign.spec.ts b/packages/util-crypto/src/nacl/sign.spec.ts new file mode 100644 index 0000000000..2e6cfd71ed --- /dev/null +++ b/packages/util-crypto/src/nacl/sign.spec.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { naclSign, naclKeypairFromSeed } from '.'; + +describe('naclSign', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a valid signature for the message', () => { + expect( + naclSign( + new Uint8Array([0x61, 0x62, 0x63, 0x64]), + naclKeypairFromSeed( + stringToU8a('12345678901234567890123456789012') + ) + ) + ).toEqual( + new Uint8Array([28, 58, 206, 239, 249, 70, 59, 191, 166, 40, 219, 218, 235, 170, 25, 79, 10, 94, 9, 197, 34, 126, 1, 150, 246, 68, 28, 238, 36, 26, 172, 163, 168, 90, 202, 211, 126, 246, 57, 212, 43, 24, 88, 197, 240, 113, 118, 76, 37, 81, 91, 110, 236, 50, 144, 134, 100, 223, 220, 238, 34, 185, 211, 7]) + ); + }); +}); diff --git a/packages/util-crypto/src/nacl/sign.ts b/packages/util-crypto/src/nacl/sign.ts new file mode 100644 index 0000000000..f8b2471882 --- /dev/null +++ b/packages/util-crypto/src/nacl/sign.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import nacl from 'tweetnacl'; +import { assert } from '@plugnet/util'; +import { isReady, ed25519Sign } from '@plugnet/wasm-crypto'; + +/** + * @name naclSign + * @summary Signs a message using the supplied secretKey + * @description + * Returns message signature of `message`, using the `secretKey`. + * @example + *
+ * + * ```javascript + * import { naclSign } from '@plugnet/util-crypto'; + * + * naclSign([...], [...]); // => [...] + * ``` + */ +export default function naclSign (message: Uint8Array, { publicKey, secretKey }: Partial): Uint8Array { + assert(secretKey, 'Expected valid secretKey'); + + return isReady() + ? ed25519Sign(publicKey as Uint8Array, (secretKey as Uint8Array).subarray(0, 32), message) + : nacl.sign.detached(message, secretKey as Uint8Array); +} diff --git a/packages/util-crypto/src/nacl/verify.spec.ts b/packages/util-crypto/src/nacl/verify.spec.ts new file mode 100644 index 0000000000..634ea94d8c --- /dev/null +++ b/packages/util-crypto/src/nacl/verify.spec.ts @@ -0,0 +1,54 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { naclVerify, naclKeypairFromSeed } from '.'; + +describe('naclSign', () => { + let publicKey: Uint8Array; + let signature: Uint8Array; + + beforeEach(async () => { + await waitReady(); + + publicKey = naclKeypairFromSeed( + stringToU8a('12345678901234567890123456789012') + ).publicKey; + signature = new Uint8Array([28, 58, 206, 239, 249, 70, 59, 191, 166, 40, 219, 218, 235, 170, 25, 79, 10, 94, 9, 197, 34, 126, 1, 150, 246, 68, 28, 238, 36, 26, 172, 163, 168, 90, 202, 211, 126, 246, 57, 212, 43, 24, 88, 197, 240, 113, 118, 76, 37, 81, 91, 110, 236, 50, 144, 134, 100, 223, 220, 238, 34, 185, 211, 7]); + }); + + it('validates a correctly signed message', () => { + expect( + naclVerify( + new Uint8Array([0x61, 0x62, 0x63, 0x64]), + signature, + publicKey + ) + ).toEqual(true); + }); + + it('fails a correctly signed message (message changed)', () => { + expect( + naclVerify( + new Uint8Array([0x61, 0x62, 0x63, 0x64, 0x65]), + signature, + publicKey + ) + ).toEqual(false); + }); + + it('fails a correctly signed message (signature changed)', () => { + signature[0] = 0xff; + + expect( + naclVerify( + new Uint8Array([0x61, 0x62, 0x63, 0x64]), + signature, + publicKey + ) + ).toEqual(false); + }); +}); diff --git a/packages/util-crypto/src/nacl/verify.ts b/packages/util-crypto/src/nacl/verify.ts new file mode 100644 index 0000000000..8395380921 --- /dev/null +++ b/packages/util-crypto/src/nacl/verify.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nacl from 'tweetnacl'; +import { isReady, ed25519Verify } from '@plugnet/wasm-crypto'; + +/** + * @name naclSign + * @summary Verifies the signature on the supplied message. + * @description + * Verifies the `signature` on `message` with the supplied `plublicKey`. Returns `true` on sucess, `false` otherwise. + * @example + *
+ * + * ```javascript + * import { naclVerify } from '@plugnet/util-crypto'; + * + * naclVerify([...], [...], [...]); // => true/false + * ``` + */ +export default function naclVerify (message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean { + return isReady() + ? ed25519Verify(signature, message, publicKey) + : nacl.sign.detached.verify(message, signature, publicKey); +} diff --git a/packages/util-crypto/src/polyfill.ts b/packages/util-crypto/src/polyfill.ts new file mode 100644 index 0000000000..04693b79e2 --- /dev/null +++ b/packages/util-crypto/src/polyfill.ts @@ -0,0 +1,5 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '@polkadot/wasm-crypto/crypto-polyfill'; diff --git a/packages/util-crypto/src/random/asHex.spec.ts b/packages/util-crypto/src/random/asHex.spec.ts new file mode 100644 index 0000000000..07e211fda1 --- /dev/null +++ b/packages/util-crypto/src/random/asHex.spec.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isHex } from '@plugnet/util'; + +import { randomAsHex } from '.'; + +describe('randomAsBuffer', () => { + it('generated results does not match', () => { + expect( + randomAsHex() + ).not.toEqual( + randomAsHex() + ); + }); + + it('is a valid hex number', () => { + expect( + isHex( + randomAsHex() + ) + ).toEqual(true); + }); + + it('generates 32 bytes by default', () => { + expect( + randomAsHex() + ).toHaveLength(32 * 2 + 2); + }); + + it('generates with the supplied length', () => { + expect( + randomAsHex(66) + ).toHaveLength(66 * 2 + 2); + }); +}); diff --git a/packages/util-crypto/src/random/asHex.ts b/packages/util-crypto/src/random/asHex.ts new file mode 100644 index 0000000000..9762030956 --- /dev/null +++ b/packages/util-crypto/src/random/asHex.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import randomAsU8a from './asU8a'; + +/** + * @name randomAsHex + * @summary Creates a hex string filled with random bytes. + * @description + * Returns a hex string with the specified (optional) length filled with random bytes. + * @example + *
+ * + * ```javascript + * import { randomAsHex } from '@plugnet/util-crypto'; + * + * randomAsHex(); // => 0x... + * ``` + */ +export default function randomAsHex (length: number = 32): string { + return u8aToHex( + randomAsU8a(length) + ); +} diff --git a/packages/util-crypto/src/random/asNumber.spec.ts b/packages/util-crypto/src/random/asNumber.spec.ts new file mode 100644 index 0000000000..0ceb26d2b5 --- /dev/null +++ b/packages/util-crypto/src/random/asNumber.spec.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { randomAsNumber } from '.'; + +describe('randomAsNumber', () => { + it('generates subsequent non-matching numbers', () => { + expect( + randomAsNumber() + ).not.toEqual( + randomAsNumber() + ); + }); +}); diff --git a/packages/util-crypto/src/random/asNumber.ts b/packages/util-crypto/src/random/asNumber.ts new file mode 100644 index 0000000000..0d74549767 --- /dev/null +++ b/packages/util-crypto/src/random/asNumber.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; +import { hexToBn } from '@plugnet/util'; + +import randomAsHex from './asHex'; + +const BN_53 = new BN(0b11111111111111111111111111111111111111111111111111111); + +/** + * @name randomAsNumber + * @summary Creates a random number from random bytes. + * @description + * Returns a random number generated from the secure bytes. + * @example + *
+ * + * ```javascript + * import { randomAsNumber } from '@plugnet/util-crypto'; + * + * randomAsNumber(); // => + * ``` + */ +export default function randomAsNumber (): number { + return hexToBn( + randomAsHex(8) + ).and(BN_53).toNumber(); +} diff --git a/packages/util-crypto/src/random/asU8a.spec.ts b/packages/util-crypto/src/random/asU8a.spec.ts new file mode 100644 index 0000000000..1e4f3d159e --- /dev/null +++ b/packages/util-crypto/src/random/asU8a.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a } from '@plugnet/util'; + +import { randomAsU8a } from '.'; + +describe('randomAsU8a', () => { + it('generates a Uint8Array', () => { + expect( + isU8a(randomAsU8a()) + ).toEqual(true); + }); + + it('generated results does not match', () => { + expect( + randomAsU8a() + ).not.toEqual( + randomAsU8a() + ); + }); + + it('generates 32 bytes by default', () => { + expect( + randomAsU8a() + ).toHaveLength(32); + }); + + it('generates with the suuplied length', () => { + expect( + randomAsU8a(66) + ).toHaveLength(66); + }); +}); diff --git a/packages/util-crypto/src/random/asU8a.ts b/packages/util-crypto/src/random/asU8a.ts new file mode 100644 index 0000000000..8f85c0edf9 --- /dev/null +++ b/packages/util-crypto/src/random/asU8a.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nacl from 'tweetnacl'; + +/** + * @name randomAsU8a + * @summary Creates a Uint8Array filled with random bytes. + * @description + * Returns a `Uint8Array` with the specified (optional) length filled with random bytes. + * @example + *
+ * + * ```javascript + * import { randomAsU8a } from '@plugnet/util-crypto'; + * + * randomAsU8a(); // => Uint8Array([...]) + * ``` + */ +export default function randomAsU8a (length: number = 32): Uint8Array { + return nacl.randomBytes(length); +} diff --git a/packages/util-crypto/src/random/index.ts b/packages/util-crypto/src/random/index.ts new file mode 100644 index 0000000000..f2ec83bbb9 --- /dev/null +++ b/packages/util-crypto/src/random/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Returns a sequence of secure random bytes in a variety of formats + */ + +export { default as randomAsHex } from './asHex'; +export { default as randomAsNumber } from './asNumber'; +export { default as randomAsU8a } from './asU8a'; diff --git a/packages/util-crypto/src/schnorrkel/deriveHard.ts b/packages/util-crypto/src/schnorrkel/deriveHard.ts new file mode 100644 index 0000000000..de0ce190f6 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/deriveHard.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import '../polyfill'; + +import { sr25519DeriveKeypairHard } from '@plugnet/wasm-crypto'; + +import keypairFromU8a from './keypair/fromU8a'; +import keypairToU8a from './keypair/toU8a'; + +export default function deriveHard (keypair: Keypair, chainCode: Uint8Array): Keypair { + return keypairFromU8a( + sr25519DeriveKeypairHard(keypairToU8a(keypair), chainCode) + ); +} diff --git a/packages/util-crypto/src/schnorrkel/deriveSoft.ts b/packages/util-crypto/src/schnorrkel/deriveSoft.ts new file mode 100644 index 0000000000..95be091e0e --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/deriveSoft.ts @@ -0,0 +1,18 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import '../polyfill'; + +import { sr25519DeriveKeypairSoft } from '@plugnet/wasm-crypto'; + +import keypairFromU8a from './keypair/fromU8a'; +import keypairToU8a from './keypair/toU8a'; + +export default function deriveSoft (keypair: Keypair, chainCode: Uint8Array): Keypair { + return keypairFromU8a( + sr25519DeriveKeypairSoft(keypairToU8a(keypair), chainCode) + ); +} diff --git a/packages/util-crypto/src/schnorrkel/index.ts b/packages/util-crypto/src/schnorrkel/index.ts new file mode 100644 index 0000000000..2a721b3f77 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/index.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +export { default as schnorrkelDeriveHard } from './deriveHard'; +export { default as schnorrkelDeriveSoft } from './deriveSoft'; +export { default as schnorrkelKeypairFromSeed } from './keypair/fromSeed'; +export { default as schnorrkelSign } from './sign'; +export { default as schnorrkelVerify } from './verify'; diff --git a/packages/util-crypto/src/schnorrkel/keypair/fromSeed.spec.ts b/packages/util-crypto/src/schnorrkel/keypair/fromSeed.spec.ts new file mode 100644 index 0000000000..d4f5b80cf7 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/keypair/fromSeed.spec.ts @@ -0,0 +1,39 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a, u8aToHex } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { mnemonicToMiniSecret } from '../../mnemonic'; +import { schnorrkelKeypairFromSeed } from '..'; +import tests from './testing'; + +describe('schnorrkelKeypairFromSeed', () => { + const TEST = stringToU8a('12345678901234567890123456789012'); + const RESULT = { + publicKey: new Uint8Array([116, 28, 8, 160, 111, 65, 197, 150, 96, 143, 103, 116, 37, 155, 217, 4, 51, 4, 173, 250, 93, 62, 234, 98, 118, 11, 217, 190, 151, 99, 77, 99]), + secretKey: new Uint8Array([240, 16, 102, 96, 195, 221, 162, 63, 22, 218, 169, 172, 91, 129, 27, 150, 48, 119, 245, 188, 10, 248, 159, 133, 128, 79, 13, 232, 228, 36, 240, 80, 249, 141, 102, 243, 148, 66, 80, 111, 249, 71, 253, 145, 31, 24, 199, 167, 165, 218, 99, 154, 99, 232, 211, 180, 226, 51, 247, 65, 67, 217, 81, 193]) + }; + + beforeEach(async () => { + return waitReady(); + }); + + it('generates a valid publicKey/secretKey pair (u8a)', () => { + expect( + schnorrkelKeypairFromSeed(TEST) + ).toEqual(RESULT); + }); + + tests.forEach(([mnemonic, , , secret], index) => { + it(`creates valid against known (${index})`, () => { + const seed = mnemonicToMiniSecret(mnemonic, 'Substrate'); + const pair = schnorrkelKeypairFromSeed(seed); + + expect( + u8aToHex(pair.secretKey) + ).toEqual(secret); + }); + }); +}); diff --git a/packages/util-crypto/src/schnorrkel/keypair/fromSeed.ts b/packages/util-crypto/src/schnorrkel/keypair/fromSeed.ts new file mode 100644 index 0000000000..d775d6f6a6 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/keypair/fromSeed.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import '../../polyfill'; + +import { sr25519KeypairFromSeed } from '@plugnet/wasm-crypto'; + +import keypairFromU8a from './fromU8a'; + +/** + * @name schnorrkelKeypairFromSeed + * @description Returns a object containing a `publicKey` & `secretKey` generated from the supplied seed. + */ +export default function schnorrkelKeypairFromSeed (seed: Uint8Array): Keypair { + return keypairFromU8a( + sr25519KeypairFromSeed(seed) + ); +} diff --git a/packages/util-crypto/src/schnorrkel/keypair/fromU8a.ts b/packages/util-crypto/src/schnorrkel/keypair/fromU8a.ts new file mode 100644 index 0000000000..bdf4de5bc2 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/keypair/fromU8a.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +const SEC_LEN = 64; +const PUB_LEN = 32; + +export default function schnorrkelKeypairFromU8a (full: Uint8Array): Keypair { + return { + publicKey: full.slice(SEC_LEN, SEC_LEN + PUB_LEN), + secretKey: full.slice(0, SEC_LEN) + }; +} diff --git a/packages/util-crypto/src/schnorrkel/keypair/testing.ts b/packages/util-crypto/src/schnorrkel/keypair/testing.ts new file mode 100644 index 0000000000..f45850f5df --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/keypair/testing.ts @@ -0,0 +1,155 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// mnemonic, entropy, seed, secret (expanded) +type Test = [string, string, string, string]; + +const tests: Array = [ + [ + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about', + '0x00000000000000000000000000000000', + '0x44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453', + '0xb0b3174fe43c15938bb0d0cc5b6f7ac7295f557ee1e6fdeb24fb73f4e0cb2b6ec40ffb9da4af6d411eae8e292750fd105ff70fe93f337b5b590f5a9d9030c750' + ], + [ + 'legal winner thank year wave sausage worth useful legal winner thank yellow', + '0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', + '0x4313249608fe8ac10fd5886c92c4579007272cb77c21551ee5b8d60b780416850f1e26c1f4b8d88ece681cb058ab66d6182bc2ce5a03181f7b74c27576b5c8bf', + '0x20666c9dd63c5b04a6a14377579af14aba60707752d134726304d0804992e26f9092c47fbb9e14c02fd53c702c8a3cfca4735638599da5c4362e0d0560dceb58' + ], + [ + 'letter advice cage absurd amount doctor acoustic avoid letter advice cage above', + '0x80808080808080808080808080808080', + '0x27f3eb595928c60d5bc91a4d747da40ed236328183046892ed6cd5aa9ae38122acd1183adf09a89839acb1e6eaa7fb563cc958a3f9161248d5a036e0d0af533d', + '0x709e8254d0a9543c6b35b145dd23349e6369d487a1d10b0cfe09c05ff521f4691ad8bb8221339af38fc48510ec2dfc3104bb94d38f1fa241ceb252943df7b6b5' + ], + [ + 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong', + '0xffffffffffffffffffffffffffffffff', + '0x227d6256fd4f9ccaf06c45eaa4b2345969640462bbb00c5f51f43cb43418c7a753265f9b1e0c0822c155a9cabc769413ecc14553e135fe140fc50b6722c6b9df', + '0x88206f4b4102ad30ee40b4b5943c5259db77fd576d95d79eeea00160197e406308821814dea9442675a5d3fa375b3bd65ffe92be43e07dbf6bb4ab84e9d4449d' + ], + [ + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent', + '0x000000000000000000000000000000000000000000000000', + '0x44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453', + '0xb0b3174fe43c15938bb0d0cc5b6f7ac7295f557ee1e6fdeb24fb73f4e0cb2b6ec40ffb9da4af6d411eae8e292750fd105ff70fe93f337b5b590f5a9d9030c750' + ], + [ + 'legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will', + '0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', + '0xcb1d50e14101024a88905a098feb1553d4306d072d7460e167a60ccb3439a6817a0afc59060f45d999ddebc05308714733c9e1e84f30feccddd4ad6f95c8a445', + '0x50dcb74f223740d6a256000a2f1ccdb60044b39ce3aad71a3bd7761848d5f55d5a34f96e0b96ecb45d7a142e07ddfde734f9525f9f88310ab50e347da5789d3e' + ], + [ + 'letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always', + '0x808080808080808080808080808080808080808080808080', + '0x9ddecf32ce6bee77f867f3c4bb842d1f0151826a145cb4489598fe71ac29e3551b724f01052d1bc3f6d9514d6df6aa6d0291cfdf997a5afdb7b6a614c88ab36a', + '0x88112947a30e864b511838b6daf6e1e13801ae003d6d9b73eb5892c355f7e37ab3bb71200092d004467b06fe67bc153ee4e2bb7af2f544815b0dde276d2dae75' + ], + [ + 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when', + '0xffffffffffffffffffffffffffffffffffffffffffffffff', + '0x8971cb290e7117c64b63379c97ed3b5c6da488841bd9f95cdc2a5651ac89571e2c64d391d46e2475e8b043911885457cd23e99a28b5a18535fe53294dc8e1693', + '0x4859cdeda3f957b7ffcd2d59257c30e43996796f38e1be5c6136c9bf3744e047ce9a52c11793c98d0dc8caee927576ce46ef2e5f4b3f1d5e4b1344b2c31ebe8e' + ], + [ + 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art', + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x44e9d125f037ac1d51f0a7d3649689d422c2af8b1ec8e00d71db4d7bf6d127e33f50c3d5c84fa3e5399c72d6cbbbbc4a49bf76f76d952f479d74655a2ef2d453', + '0xb0b3174fe43c15938bb0d0cc5b6f7ac7295f557ee1e6fdeb24fb73f4e0cb2b6ec40ffb9da4af6d411eae8e292750fd105ff70fe93f337b5b590f5a9d9030c750' + ], + [ + 'legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title', + '0x7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', + '0x3037276a5d05fcd7edf51869eb841bdde27c574dae01ac8cfb1ea476f6bea6ef57ab9afe14aea1df8a48f97ae25b37d7c8326e49289efb25af92ba5a25d09ed3', + '0xe8962ace15478f69fa42ddd004aad2c285c9f5a02e0712860e83fbec041f89489046aa57b21db2314d93aeb4a2d7cbdab21d4856e8e151894abd17fb04ae65e1' + ], + [ + 'letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless', + '0x8080808080808080808080808080808080808080808080808080808080808080', + '0x2c9c6144a06ae5a855453d98c3dea470e2a8ffb78179c2e9eb15208ccca7d831c97ddafe844ab933131e6eb895f675ede2f4e39837bb5769d4e2bc11df58ac42', + '0x78667314bf1e52e38d29792cdf294efcaddadc4fa9ce48c5f2bef4daad7ed95d1db960d6f6f895c1a9d2a3ddcc0398ba6578580ea1f03f65ea9a68e97cf3f840' + ], + [ + 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote', + '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '0x047e89ef7739cbfe30da0ad32eb1720d8f62441dd4f139b981b8e2d0bd412ed4eb14b89b5098c49db2301d4e7df4e89c21e53f345138e56a5e7d63fae21c5939', + '0xe09f4ae0d4e22f6c9bbc4251c880e73d93d10fdf7f152c393ce36e4e942b3155a6f4b48e6c6ebce902a10d4ab46ef59cd154c15aeb8f7fcd4e1e26342d40806d' + ], + [ + 'ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic', + '0x9e885d952ad362caeb4efe34a8e91bd2', + '0xf4956be6960bc145cdab782e649a5056598fd07cd3f32ceb73421c3da27833241324dc2c8b0a4d847eee457e6d4c5429f5e625ece22abaa6a976e82f1ec5531d', + '0xb0eb046f48eacb7ad6c4da3ff92bfc29c9ad471bae3e554d5d63e58827160c70c7e5165598761f96b5659ab28c474f50e89ee13c67e30bca40fdcf4335835649' + ], + [ + 'gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog', + '0x6610b25967cdcca9d59875f5cb50b0ea75433311869e930b', + '0xfbcc5229ade0c0ff018cb7a329c5459f91876e4dde2a97ddf03c832eab7f26124366a543f1485479c31a9db0d421bda82d7e1fe562e57f3533cb1733b001d84d', + '0xd8da734285a13967647dd906288e1ac871e1945d0c6b72fa259de4051a9b75431be0c4eb40b1ca38c780f445e3e2809282b9efef4dcc3538355e68094f1e79fa' + ], + [ + 'hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length', + '0x68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c', + '0x7c60c555126c297deddddd59f8cdcdc9e3608944455824dd604897984b5cc369cad749803bb36eb8b786b570c9cdc8db275dbe841486676a6adf389f3be3f076', + '0x883b72fa7fb06b6abd0fc2cdb0018b3578e086e93074256bbbb8c68e53c04a56391bc0d19d7b2fa22a8148ccbe191d969c4323faca1935a576cc1b24301f203a' + ], + [ + 'scheme spot photo card baby mountain device kick cradle pact join borrow', + '0xc0ba5a8e914111210f2bd131f3d5e08d', + '0xc12157bf2506526c4bd1b79a056453b071361538e9e2c19c28ba2cfa39b5f23034b974e0164a1e8acd30f5b4c4de7d424fdb52c0116bfc6a965ba8205e6cc121', + '0x7039a64150089f8d43188af964c7b8e2b8c9ba20aede085baca5672e978a47576c7193c3e557f37cdeeee5e5131b854e4309efc55259b050474e1f0884a7a621' + ], + [ + 'horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave', + '0x6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3', + '0x23766723e970e6b79dec4d5e4fdd627fd27d1ee026eb898feb9f653af01ad22080c6f306d1061656d01c4fe9a14c05f991d2c7d8af8730780de4f94cd99bd819', + '0xe07a1f3073edad5b63585cdf1d5e6f8e50e3145de550fc8eb1fb430cce62d76d251904272c5d25fd634615d413bb31a2bc7b5d6eeb2f6ddc68a2b95ac6bd49bc' + ], + [ + 'panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside', + '0x9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863', + '0xf4c83c86617cb014d35cd87d38b5ef1c5d5c3d58a73ab779114438a7b358f457e0462c92bddab5a406fe0e6b97c71905cf19f925f356bc673ceb0e49792f4340', + '0x607f8595266ac0d4aa91bf4fddbd2a868889317f40099979be9743c46c418976e6ff3717bd11b94b418f91c8b88eae142cecb19104820997ddf5a379dd9da5ae' + ], + [ + 'cat swing flag economy stadium alone churn speed unique patch report train', + '0x23db8160a31d3e0dca3688ed941adbf3', + '0x719d4d4de0638a1705bf5237262458983da76933e718b2d64eb592c470f3c5d222e345cc795337bb3da393b94375ff4a56cfcd68d5ea25b577ee9384d35f4246', + '0xd078b66bb357f1f06e897a6fdfa2f3dfb0da05836ded1fd0793373068b7e854e783a548a6d194f142e1ba78bf42a49fa58e3673b363ba6f6494efffa28f168df' + ], + [ + 'light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access', + '0x8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0', + '0x7ae1291db32d16457c248567f2b101e62c5549d2a64cd2b7605d503ec876d58707a8d663641e99663bc4f6cc9746f4852e75e7e54de5bc1bd3c299c9a113409e', + '0x5095fe4d0144b06e82aa4753d595fd10de9bba3733eba8ce0784417182317e725fac31b2fb53f4856a5e38866501425b485f4d2eaf2666a9f20ae68f4331ed2c' + ], + [ + 'all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform', + '0x066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad', + '0xa911a5f4db0940b17ecb79c4dcf9392bf47dd18acaebdd4ef48799909ebb49672947cc15f4ef7e8ef47103a1a91a6732b821bda2c667e5b1d491c54788c69391', + '0x8844cb50f3ba8030ab61afee623534836d4ea3677d42bae470fc5e251ea0ca7ec9ea65c8c40be191c7c8683165848279ced81f3a121c9450078a496b6c59f610' + ], + [ + 'vessel ladder alter error federal sibling chat ability sun glass valve picture', + '0xf30f8c1da665478f49b001d94c5fc452', + '0x4e2314ca7d9eebac6fe5a05a5a8d3546bc891785414d82207ac987926380411e559c885190d641ff7e686ace8c57db6f6e4333c1081e3d88d7141a74cf339c8f', + '0x18917f0c7480c95cd4d98bdc7df773c366d33590252707da1358eb58b43a7b765e3c513878541bfbfb466bb4206f581edf9bf601409c72afac130bcc8b5661b5' + ], + [ + 'scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump', + '0xc10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05', + '0x7a83851102849edc5d2a3ca9d8044d0d4f00e5c4a292753ed3952e40808593251b0af1dd3c9ed9932d46e8608eb0b928216a6160bd4fc775a6e6fbd493d7c6b2', + '0xb0bf86b0955413fc95144bab124e82042d0cce9c292c1bfd0874ae5a95412977e7bc109aeef33c7c90be952a83f3fe528419776520de721ef6ec9e814749c3fc' + ], + [ + 'void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold', + '0xf585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f', + '0x938ba18c3f521f19bd4a399c8425b02c716844325b1a65106b9d1593fbafe5e0b85448f523f91c48e331995ff24ae406757cff47d11f240847352b348ff436ed', + '0xc07ba4a979657576f4f7446e3bd2672c87131fa0f472a8bc1f2e9b28c11fb04c66da12cd280662196a5888d8a77178dab8034ed42b11d1654a31db6e1ff4d4c5' + ] +]; + +export default tests; diff --git a/packages/util-crypto/src/schnorrkel/keypair/toU8a.ts b/packages/util-crypto/src/schnorrkel/keypair/toU8a.ts new file mode 100644 index 0000000000..bc6e551392 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/keypair/toU8a.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../../types'; + +import { u8aConcat } from '@plugnet/util'; + +export default function schnorrkelKeypairToU8a ({ publicKey, secretKey }: Keypair): Uint8Array { + return u8aConcat(secretKey, publicKey).slice(); +} diff --git a/packages/util-crypto/src/schnorrkel/sign.ts b/packages/util-crypto/src/schnorrkel/sign.ts new file mode 100644 index 0000000000..9adba0af37 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/sign.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Keypair } from '../types'; + +import '../polyfill'; + +import { assert } from '@plugnet/util'; +import { sr25519Sign } from '@plugnet/wasm-crypto'; + +/** + * @name schnorrkelSign + * @description Returns message signature of `message`, using the supplied pair + */ +export default function schnorrkelSign (message: Uint8Array, { publicKey, secretKey }: Partial): Uint8Array { + assert(publicKey && publicKey.length === 32, 'Expected valid publicKey, 32-bytes'); + assert(secretKey && secretKey.length === 64, 'Expected valid secretKey, 64-bytes'); + + return sr25519Sign(publicKey as Uint8Array, secretKey as Uint8Array, message); +} diff --git a/packages/util-crypto/src/schnorrkel/signVerify.spec.ts b/packages/util-crypto/src/schnorrkel/signVerify.spec.ts new file mode 100644 index 0000000000..e56b10a08e --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/signVerify.spec.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import randomAsU8a from '../random/asU8a'; +import pairFromSeed from './keypair/fromSeed'; +import sign from './sign'; +import verify from './verify'; + +const MESSAGE = stringToU8a('this is a message'); + +describe('sign and verify', () => { + beforeEach(async () => { + return waitReady(); + }); + + it('has 64-byte signatures', () => { + const pair = pairFromSeed(randomAsU8a()); + + expect(sign(MESSAGE, pair)).toHaveLength(64); + }); + + it('can sign and verify a message', () => { + const pair = pairFromSeed(randomAsU8a()); + const signature = sign(MESSAGE, pair); + + expect(verify(MESSAGE, signature, pair.publicKey)).toBe(true); + }); +}); diff --git a/packages/util-crypto/src/schnorrkel/verify.ts b/packages/util-crypto/src/schnorrkel/verify.ts new file mode 100644 index 0000000000..7575643281 --- /dev/null +++ b/packages/util-crypto/src/schnorrkel/verify.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import '../polyfill'; + +import { sr25519Verify } from '@plugnet/wasm-crypto'; + +/** + * @name schnorrkelVerify + * @description Verifies the signature of `message`, using the supplied pair + */ +export default function schnorrkelVerify (message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean { + return sr25519Verify(signature, message, publicKey); +} diff --git a/packages/util-crypto/src/secp256k1/index.ts b/packages/util-crypto/src/secp256k1/index.ts new file mode 100644 index 0000000000..8c093b6fe4 --- /dev/null +++ b/packages/util-crypto/src/secp256k1/index.ts @@ -0,0 +1,5 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { default as secp256k1Recover } from './recover'; diff --git a/packages/util-crypto/src/secp256k1/recover.ts b/packages/util-crypto/src/secp256k1/recover.ts new file mode 100644 index 0000000000..2287199549 --- /dev/null +++ b/packages/util-crypto/src/secp256k1/recover.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import secp256k1 from 'secp256k1'; +import { u8aToBuffer, u8aToU8a } from '@plugnet/util'; + +/** + * @name secp256k1Recover + * @description Recovers a publicKey from the supplied signature + */ +export default function secp256k1Recover (message: Uint8Array, signature: Uint8Array, recovery: number): Uint8Array { + return u8aToU8a( + secp256k1.recover(u8aToBuffer(message), u8aToBuffer(signature), recovery) + ); +} diff --git a/packages/util-crypto/src/sha512/asU8a.spec.ts b/packages/util-crypto/src/sha512/asU8a.spec.ts new file mode 100644 index 0000000000..ae880fbf39 --- /dev/null +++ b/packages/util-crypto/src/sha512/asU8a.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { waitReady } from '@plugnet/wasm-crypto'; + +import { sha512AsU8a } from '.'; + +describe('sha512AsU8a', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('creates a sha-512 hash', () => { + expect( + sha512AsU8a( + Uint8Array.from([0x61, 0x62, 0x63, 0x64]) + ) + ).toEqual( + Uint8Array.from([ + 216, 2, 47, 32, 96, 173, 110, 253, 41, 122, 183, 61, 204, 83, 85, 201, 178, 20, 5, 75, 13, 23, 118, 161, 54, 166, 105, 210, 106, 125, 59, 20, 247, 58, 160, 208, 235, 255, 25, 238, 51, 51, 104, 240, 22, 75, 100, 25, 169, 109, 164, 158, 62, 72, 23, 83, 231, 233, 107, 113, 107, 220, 203, 111 + ]) + ); + }); +}); diff --git a/packages/util-crypto/src/sha512/asU8a.ts b/packages/util-crypto/src/sha512/asU8a.ts new file mode 100644 index 0000000000..b7eb99a280 --- /dev/null +++ b/packages/util-crypto/src/sha512/asU8a.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import nacl from 'tweetnacl'; +import { isReady, sha512 } from '@plugnet/wasm-crypto'; + +/** + * @name sha512AsU8a + * @summary Creates sha-512 hash of the input. + * @description + * Returns a sha-512 `Uint8Array` from the supplied data. + * @example + *
+ * + * ```javascript + * import { sha512AsU8a } from '@plugnet/util-crypto'; + * + * sha512AsU8a(Uint8Array.from([...])); // => Uint8Array([...]) + * ``` + */ +export default function sha512AsU8a (data: Uint8Array): Uint8Array { + return isReady() + ? sha512(data) + : nacl.hash(data); +} diff --git a/packages/util-crypto/src/sha512/index.ts b/packages/util-crypto/src/sha512/index.ts new file mode 100644 index 0000000000..887f59f295 --- /dev/null +++ b/packages/util-crypto/src/sha512/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Implements Sha-512 hashing functions for a variety of input and outputs + */ + +export { default as sha512AsU8a } from './asU8a'; diff --git a/packages/util-crypto/src/types.ts b/packages/util-crypto/src/types.ts new file mode 100644 index 0000000000..b010a8c13e --- /dev/null +++ b/packages/util-crypto/src/types.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type Keypair = { + publicKey: Uint8Array; + secretKey: Uint8Array; +}; + +export type Seedpair = { + publicKey: Uint8Array; + seed: Uint8Array; +}; + +export type KeypairType = 'ed25519' | 'sr25519'; diff --git a/packages/util-crypto/src/xxhash/asHex.spec.ts b/packages/util-crypto/src/xxhash/asHex.spec.ts new file mode 100644 index 0000000000..62acb13fe4 --- /dev/null +++ b/packages/util-crypto/src/xxhash/asHex.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { waitReady } from '@plugnet/wasm-crypto'; + +import { xxhashAsHex } from '.'; + +describe('xxhashAsHex', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a 64-bit value by default', () => { + expect( + xxhashAsHex('abc') + ).toEqual('0x990977adf52cbc44'); + }); + + it('returns a 128-bit value (as specified)', () => { + expect( + xxhashAsHex('abc', 128) + ).toEqual('0x990977adf52cbc440889329981caa9be'); + }); + + it('returns a 256-bit value (as specified)', () => { + expect( + xxhashAsHex('abc', 256) + ).toEqual('0x990977adf52cbc440889329981caa9bef7da5770b2b8a05303b75d95360dd62b'); + }); +}); diff --git a/packages/util-crypto/src/xxhash/asHex.ts b/packages/util-crypto/src/xxhash/asHex.ts new file mode 100644 index 0000000000..f9775d8c7b --- /dev/null +++ b/packages/util-crypto/src/xxhash/asHex.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '@plugnet/util'; + +import xxhashAsU8a from './asU8a'; + +/** + * @name xxhashAsHex + * @summary Creates a xxhash64 hex from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash64 and return the result as a hex string with the specified `bitLength`. + * @example + *
+ * + * ```javascript + * import { xxhashAsHex } from '@plugnet/util-crypto'; + * + * xxhashAsHex('abc'); // => 0x44bc2cf5ad770999 + * ``` + */ +export default function xxhashAsHex (data: Buffer | Uint8Array | string, bitLength: number = 64): string { + return u8aToHex( + xxhashAsU8a(data, bitLength) + ); +} diff --git a/packages/util-crypto/src/xxhash/asU8a.spec.ts b/packages/util-crypto/src/xxhash/asU8a.spec.ts new file mode 100644 index 0000000000..0326f409db --- /dev/null +++ b/packages/util-crypto/src/xxhash/asU8a.spec.ts @@ -0,0 +1,38 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a } from '@plugnet/util'; +import { waitReady } from '@plugnet/wasm-crypto'; + +import { xxhashAsU8a } from '.'; + +describe('xxhashAsU8a', () => { + beforeEach(async () => { + await waitReady(); + }); + + it('returns a 64-bit value by default', () => { + expect( + xxhashAsU8a('abc') + ).toEqual( + hexToU8a('0x990977adf52cbc44') + ); + }); + + it('returns a 128-bit value (as specified)', () => { + expect( + xxhashAsU8a('abc', 128) + ).toEqual( + hexToU8a('0x990977adf52cbc440889329981caa9be') + ); + }); + + it('returns a 256-bit value (as specified)', () => { + expect( + xxhashAsU8a('abc', 256) + ).toEqual( + hexToU8a('0x990977adf52cbc440889329981caa9bef7da5770b2b8a05303b75d95360dd62b') + ); + }); +}); diff --git a/packages/util-crypto/src/xxhash/asU8a.ts b/packages/util-crypto/src/xxhash/asU8a.ts new file mode 100644 index 0000000000..f863b48d5e --- /dev/null +++ b/packages/util-crypto/src/xxhash/asU8a.ts @@ -0,0 +1,38 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToU8a } from '@plugnet/util'; +import { isReady, twox } from '@plugnet/wasm-crypto'; + +import xxhash64AsBn from './xxhash64/asBn'; + +/** + * @name xxhashAsU8a + * @summary Creates a xxhash64 u8a from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash64 and return the result as a `Uint8Array` with the specified `bitLength`. + * @example + *
+ * + * ```javascript + * import { xxhashAsU8a } from '@plugnet/util-crypto'; + * + * xxhashAsU8a('abc'); // => 0x44bc2cf5ad770999 + * ``` + */ +export default function xxhashAsU8a (data: Buffer | Uint8Array | string, bitLength: number = 64): Uint8Array { + const iterations = Math.ceil(bitLength / 64); + + if (isReady()) { + return twox(u8aToU8a(data), iterations); + } + + const u8a = new Uint8Array(Math.ceil(bitLength / 8)); + + for (let seed = 0; seed < iterations; seed++) { + u8a.set(xxhash64AsBn(data, seed).toArray('le', 8), seed * 8); + } + + return u8a; +} diff --git a/packages/util-crypto/src/xxhash/index.ts b/packages/util-crypto/src/xxhash/index.ts new file mode 100644 index 0000000000..3d783ec7a0 --- /dev/null +++ b/packages/util-crypto/src/xxhash/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Create xxhash64 values with specified bitlengths + */ + +export { default as xxhashAsHex } from './asHex'; +export { default as xxhashAsU8a } from './asU8a'; diff --git a/packages/util-crypto/src/xxhash/xxhash64/.nodoc b/packages/util-crypto/src/xxhash/xxhash64/.nodoc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/util-crypto/src/xxhash/xxhash64/asBn.spec.ts b/packages/util-crypto/src/xxhash/xxhash64/asBn.spec.ts new file mode 100644 index 0000000000..9d803e4f91 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asBn.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { xxhash64AsBn } from '.'; + +describe('xxhash64AsBn', () => { + it('creates the correct BN output', () => { + expect( + xxhash64AsBn('abcd', 0xabcd).toString(16) + ).toEqual('e29f70f8b8c96df7'); + }); +}); diff --git a/packages/util-crypto/src/xxhash/xxhash64/asBn.ts b/packages/util-crypto/src/xxhash/xxhash64/asBn.ts new file mode 100644 index 0000000000..9c9ec15883 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asBn.ts @@ -0,0 +1,28 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import xxhash64AsRaw from './asRaw'; + +/** + * @name xxhash64AsBn + * @summary Creates a xxhash BN from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash and return the result as a BN. + * @example + *
+ * + * ```javascript + * import { xxhash64AsNumber } from '@plugnet/util-crypto'; + * + * xxhash64AsBn('abcd', 0xabcd)); // => new BN(0xe29f70f8b8c96df7) + * ``` + */ +export default function xxhash64AsBn (data: Buffer | Uint8Array | string, seed: number): BN { + return new BN( + xxhash64AsRaw(data, seed), + 16 + ); +} diff --git a/packages/util-crypto/src/xxhash/xxhash64/asHex.spec.ts b/packages/util-crypto/src/xxhash/xxhash64/asHex.spec.ts new file mode 100644 index 0000000000..6d9d941f2b --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asHex.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { xxhash64AsHex } from '.'; + +describe('xxhash64AsHex', () => { + it('creates the correct hex output', () => { + expect( + xxhash64AsHex('abcd', 0xabcd) + ).toEqual('0xe29f70f8b8c96df7'); + }); +}); diff --git a/packages/util-crypto/src/xxhash/xxhash64/asHex.ts b/packages/util-crypto/src/xxhash/xxhash64/asHex.ts new file mode 100644 index 0000000000..d4b45b5093 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asHex.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexAddPrefix } from '@plugnet/util'; + +import xxhash64AsRaw from './asRaw'; + +/** + * @name xxhash64AsHex + * @summary Creates a xxhash hex from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash and return the result as a hex string. + * @example + *
+ * + * ```javascript + * import { xxhash64AsHex } from '@plugnet/util-crypto'; + * + * xxhash64AsHex('abcd', 0xabcd)); // => 0xe29f70f8b8c96df7 + * ``` + */ +export default function xxhash64AsHex (data: Buffer | Uint8Array | string, seed: number): string { + return hexAddPrefix( + xxhash64AsRaw(data, seed) + ); +} diff --git a/packages/util-crypto/src/xxhash/xxhash64/asRaw.spec.ts b/packages/util-crypto/src/xxhash/xxhash64/asRaw.spec.ts new file mode 100644 index 0000000000..94f8d9b229 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asRaw.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { xxhash64AsRaw } from '.'; + +describe('xxhash64AsRaw', () => { + it('creates the correct non-prefixed hex output', () => { + expect( + xxhash64AsRaw('abcd', 0xabcd) + ).toEqual('e29f70f8b8c96df7'); + }); +}); diff --git a/packages/util-crypto/src/xxhash/xxhash64/asRaw.ts b/packages/util-crypto/src/xxhash/xxhash64/asRaw.ts new file mode 100644 index 0000000000..2249ab9fd8 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asRaw.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import xxhash64AsValue from './asValue'; + +/** + * @name xxhash64AsRaw + * @summary Creates a xxhash non-prefixed hex from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash and return the result as a non-prefixed hex string. + * @example + *
+ * + * ```javascript + * import { xxhash64AsRaw } from '@plugnet/util-crypto'; + * + * xxhash64AsRaw('abcd', 0xabcd)); // => e29f70f8b8c96df7 + * ``` + */ +export default function xxhash64AsRaw (data: Buffer | Uint8Array | string, seed: number): string { + return xxhash64AsValue(data, seed).toString(16); +} diff --git a/packages/util-crypto/src/xxhash/xxhash64/asValue.spec.ts b/packages/util-crypto/src/xxhash/xxhash64/asValue.spec.ts new file mode 100644 index 0000000000..c0eb0e02a5 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asValue.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import xxhash64AsValue from './asValue'; + +describe('xxhash64AsValue', () => { + it('creates the correct output (string input)', () => { + expect( + xxhash64AsValue('abcd', 0xabcd).toString(16) + ).toEqual('e29f70f8b8c96df7'); + }); + + it('creates the correct output (Buffer input)', () => { + expect( + xxhash64AsValue(Buffer.from([0x61, 0x62, 0x63, 0x64]), 0xabcd).toString(16) + ).toEqual('e29f70f8b8c96df7'); + }); + + it('creates the correct output (Uint8Array input)', () => { + expect( + xxhash64AsValue(new Uint8Array([0x61, 0x62, 0x63, 0x64]), 0xabcd).toString(16) + ).toEqual('e29f70f8b8c96df7'); + }); +}); diff --git a/packages/util-crypto/src/xxhash/xxhash64/asValue.ts b/packages/util-crypto/src/xxhash/xxhash64/asValue.ts new file mode 100644 index 0000000000..2ce78380b6 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/asValue.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import xxhashjs from 'xxhashjs'; +import { isBuffer, isString, u8aToBuffer } from '@plugnet/util'; + +/** + * @name xxhash64AsValue + * @summary Creates a hex number from the input. + * @description + * From either a `string`, `Uint8Array` or a `Buffer` input, create the xxhash and return the result as a hex number + * @example + *
+ * + * ```javascript + * import { xxhash64AsValue } from '@plugnet/util-crypto'; + * + * xxhash64AsValue('abcd', 0xabcd)); // => e29f70f8b8c96df7 + * ``` + */ +export default function xxhash64AsValue (data: Buffer | Uint8Array | string, seed: number): number { + if (isBuffer(data) || isString(data)) { + // @ts-ignore Buffer is ArrayBuffer underlying + return xxhashjs.h64(data, seed); + } + + // @ts-ignore conversion works, yields correct result + return xxhashjs.h64( + u8aToBuffer(data), + seed + ) as any; +} diff --git a/packages/util-crypto/src/xxhash/xxhash64/index.ts b/packages/util-crypto/src/xxhash/xxhash64/index.ts new file mode 100644 index 0000000000..1f3842f745 --- /dev/null +++ b/packages/util-crypto/src/xxhash/xxhash64/index.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/util-crypto authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import xxhash64AsBn from './asBn'; +import xxhash64AsHex from './asHex'; +import xxhash64AsRaw from './asRaw'; + +/** + * @summary Create [XXHash](http://cyan4973.github.io/xxHash/) values as BN, hex & number output + */ +export { + xxhash64AsBn, + xxhash64AsHex, + xxhash64AsRaw +}; diff --git a/packages/util/LICENSE b/packages/util/LICENSE new file mode 100644 index 0000000000..0d381b2e97 --- /dev/null +++ b/packages/util/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/util/README.md b/packages/util/README.md new file mode 100644 index 0000000000..d3265263d5 --- /dev/null +++ b/packages/util/README.md @@ -0,0 +1,21 @@ +# @polkadot/util + +Various useful utility functions that are used across all projects in the [@polkadot](https://polkadot.js.org) namespace. It provides utility functions with additional safety checks, allowing not only for consistent coding, but also reducing the general boilerplate. + +## Usage + +Installation - + +``` +yarn add @polkadot/util +``` + +Functions can be imported directly from the package, e.g. + +```js +import { isHex } from '@polkadot/util'; +``` + +## Utilities + +[Utilities](SUMMARY.md) diff --git a/packages/util/package.json b/packages/util/package.json new file mode 100644 index 0000000000..1091c438e1 --- /dev/null +++ b/packages/util/package.json @@ -0,0 +1,41 @@ +{ + "name": "@plugnet/util", + "version": "0.76.1", + "description": "A collection of useful utilities for @plugnet", + "main": "index.js", + "keywords": [ + "Plugnet" + ], + "author": "Jaco Greeff ", + "maintainers": [ + "Jaco Greeff " + ], + "contributors": [], + "license": "Apache-2.0", + "engines": { + "node": ">=6.4" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/common.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/common/issues" + }, + "homepage": "https://github.com/polkadot-js/common/tree/master/packages/util#readme", + "dependencies": { + "@babel/runtime": "^7.4.3", + "@types/bn.js": "^4.11.5", + "@types/deasync": "^0.1.0", + "@types/ip-regex": "^3.0.0", + "bn.js": "^4.11.8", + "camelcase": "^5.3.0", + "chalk": "^2.4.2", + "ip-regex": "^4.0.0", + "moment": "^2.24.0" + } +} diff --git a/packages/util/src/array/filter.spec.ts b/packages/util/src/array/filter.spec.ts new file mode 100644 index 0000000000..77a236610d --- /dev/null +++ b/packages/util/src/array/filter.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { arrayFilter } from '.'; + +describe('filterArray', () => { + it('filters arrays, removing undefined', () => { + expect( + arrayFilter([0, '', null, false, void 0, NaN]) + ).toEqual([0, '', null, false, NaN]); + }); + + it('filters arrays, removing undefined & null (allowNull = false)', () => { + expect( + arrayFilter([0, '', null, false, void 0, NaN], false) + ).toEqual([0, '', false, NaN]); + }); +}); diff --git a/packages/util/src/array/filter.ts b/packages/util/src/array/filter.ts new file mode 100644 index 0000000000..9a95d20cb1 --- /dev/null +++ b/packages/util/src/array/filter.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isNull from '../is/null'; +import isUndefined from '../is/undefined'; + +/** + * @name arrayFilter + * @summary Filters undefined and (optionally) null values from an array + * @description + * Returns a new array with all `undefined` values removed. Optionally, when `allowNulls = false`, it removes the `null` values as well + * @example + *
+ * + * ```javascript + * import { arrayFilter } from '@plugnet/util'; + * + * arrayFilter([0, void 0, true, null, false, '']); // [0, true, null, false, ''] + * arrayFilter([0, void 0, true, null, false, ''], false); // [0, true, false, ''] + * ``` + */ +export default function arrayFilter (array: Array, allowNulls: boolean = true): Array { + return array.filter((value) => { + return !isUndefined(value) && (allowNulls || !isNull(value)); + }); +} diff --git a/packages/util/src/array/index.ts b/packages/util/src/array/index.ts new file mode 100644 index 0000000000..98a6249cf4 --- /dev/null +++ b/packages/util/src/array/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods that operates on arrays + */ + +export { default as arrayFilter } from './filter'; diff --git a/packages/util/src/assert.spec.ts b/packages/util/src/assert.spec.ts new file mode 100644 index 0000000000..4fecd6724d --- /dev/null +++ b/packages/util/src/assert.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert } from '.'; + +describe('assert', () => { + it('should not throw an error when test is true', () => { + expect( + assert(true, 'nothing should be thrown') + ).toEqual(true); + }); + + it('should throw an error when test is not true', () => { + expect( + () => assert(false, 'error thrown') + ).toThrow(/error thrown/); + }); + + it('should throw an error when message: () => string', () => { + expect( + () => assert(false, () => 'message from function') + ).toThrow(/message from function/); + }); + + it('throws a valid constructed ExtError', () => { + try { + assert(false, 'error', -666, { some: 'data' }); + } catch (error) { + expect(error.code).toEqual(-666); + expect(error.data).toEqual({ some: 'data' }); + expect(error.message).toEqual('error'); + } + }); +}); diff --git a/packages/util/src/assert.ts b/packages/util/src/assert.ts new file mode 100644 index 0000000000..c14492d15d --- /dev/null +++ b/packages/util/src/assert.ts @@ -0,0 +1,38 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import ExtError from './ext/error'; +import isFunction from './is/function'; + +type MessageFn = () => string; + +type Falsy = null | undefined | false | 0 | ''; // No NaN type + +/** + * @name assert + * @summary Checks for a valid test, if not ExtError is thrown. + * @description + * Checks that `test` is a truthy value. If value is falsy (`null`, `undefined`, `false`, ...), it throws an ExtError with the supplied `message` and an optional `code` and `data`. When `test` passes, `true` is returned. + * @example + *
+ * + * ```javascript + * const { assert } from '@plugnet/util'; + * + * assert(true, 'True should be true'); // true returned + * assert(false, 'False should not be true'); // ExtError thrown + * assert(false, () => 'message'); // ExtError with 'message' + * ``` + */ +export default function assert (test: Falsy | T, message: string | MessageFn, code: number = ExtError.CODES.ASSERT, data?: any): test is T { + if (test) { + return true; + } + + if (isFunction(message)) { + message = message(); + } + + throw new ExtError(message, code, data); +} diff --git a/packages/util/src/assertSingletonPackage.spec.ts b/packages/util/src/assertSingletonPackage.spec.ts new file mode 100644 index 0000000000..9fc763c696 --- /dev/null +++ b/packages/util/src/assertSingletonPackage.spec.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assertSingletonPackage } from '.'; + +describe('assertSingletonPackage', () => { + const NAME = 'assertSingletonPackage'; + + it('should not throw the first time', () => { + expect( + () => assertSingletonPackage(NAME) + ).not.toThrow(); + }); + + it('should throw the second time', () => { + expect( + () => assertSingletonPackage(NAME) + ).toThrow(/Multiple versions of assertSingletonPackage detected/); + }); +}); diff --git a/packages/util/src/assertSingletonPackage.ts b/packages/util/src/assertSingletonPackage.ts new file mode 100644 index 0000000000..1daf03fda1 --- /dev/null +++ b/packages/util/src/assertSingletonPackage.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isUndefined from './is/undefined'; +import assert from './assert'; + +/** + * @name assertSingletonPackage + * @summary Checks that a specific package is only imported once + */ +export default function assertSingletonPackage (name: string): void { + // tslint:disable-next-line + const _global: any = typeof window !== 'undefined' + ? window + : global; + + if (!_global.__polkadotjs) { + _global.__polkadotjs = {}; + } + + assert(isUndefined(_global.__polkadotjs[name]), `Multiple versions of ${name} detected, ensure that there is only version one in your dependency tree`); + + _global.__polkadotjs[name] = true; +} diff --git a/packages/util/src/bn/fromHex.spec.ts b/packages/util/src/bn/fromHex.spec.ts new file mode 100644 index 0000000000..cd5db3affd --- /dev/null +++ b/packages/util/src/bn/fromHex.spec.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isFunction from '../is/function'; + +import { bnFromHex } from '.'; + +describe('bnFromHex', () => { + it('exists as a function', () => { + expect( + isFunction(bnFromHex) + ).toEqual(true); + }); +}); diff --git a/packages/util/src/bn/fromHex.ts b/packages/util/src/bn/fromHex.ts new file mode 100644 index 0000000000..398d3ab9b6 --- /dev/null +++ b/packages/util/src/bn/fromHex.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import bnFromHex from '../hex/toBn'; + +/** + * @name bnFromHex + * @summary Creates a BN.js bignumber object from a hex string. + * @alias hex/toBn + */ +export default bnFromHex; diff --git a/packages/util/src/bn/index.ts b/packages/util/src/bn/index.ts new file mode 100644 index 0000000000..66f723c5d3 --- /dev/null +++ b/packages/util/src/bn/index.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods to convert to and from `BN` objects + */ + +export { default as bnFromHex } from './fromHex'; +export { default as bnToBn } from './toBn'; +export { default as bnToHex } from './toHex'; +export { default as bnToU8a } from './toU8a'; diff --git a/packages/util/src/bn/toBn.spec.ts b/packages/util/src/bn/toBn.spec.ts new file mode 100644 index 0000000000..194245485b --- /dev/null +++ b/packages/util/src/bn/toBn.spec.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import { bnToBn } from '.'; + +describe('bnToBn', () => { + it('converts null values to 0x00', () => { + expect( + bnToBn(null) + ).toEqual(new BN(0)); + }); + + it('converts BN values to BN', () => { + expect( + bnToBn(new BN(128)) + ).toEqual(new BN(128)); + }); + + it('converts number values to BN', () => { + expect( + bnToBn(128) + ).toEqual(new BN(128)); + }); +}); diff --git a/packages/util/src/bn/toBn.ts b/packages/util/src/bn/toBn.ts new file mode 100644 index 0000000000..0df72c27fc --- /dev/null +++ b/packages/util/src/bn/toBn.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +/** + * @name bnToBn + * @summary Creates a BN value from a BN.js bignumber or number input. + * @description + * `null` inputs returns a `0x0` result, BN values returns the value, numnbers returns a BN representation. + * @example + *
+ * + * ```javascript + * import BN from 'bn.js'; + * import { bnToBn } from '@plugnet/util'; + * + * bnToBn(0x1234); // => BN(0x1234) + * bnToBn(new BN(0x1234)); // => BN(0x1234) + * ``` + */ +export default function bnToBn (value?: BN | number | null): BN { + if (!value) { + return new BN(0); + } + + return BN.isBN(value) + ? value + : new BN(value); +} diff --git a/packages/util/src/bn/toHex.spec.ts b/packages/util/src/bn/toHex.spec.ts new file mode 100644 index 0000000000..a34bc00dfa --- /dev/null +++ b/packages/util/src/bn/toHex.spec.ts @@ -0,0 +1,53 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import { bnToHex } from '.'; + +describe('bnToHex', () => { + it('converts null values to 0x00', () => { + expect( + bnToHex(null) + ).toBe('0x00'); + }); + + it('converts BN values to a prefixed hex representation', () => { + expect( + bnToHex(new BN(128)) + ).toBe('0x80'); + }); + + it('converts BN values to a prefixed hex representation (bitLength)', () => { + expect( + bnToHex(new BN(128), 16) + ).toBe('0x0080'); + }); + + it('converts BN values to a prefixed hex representation (LE)', () => { + expect( + bnToHex(new BN(128), { bitLength: 16, isLe: true }) + ).toBe('0x8000'); + }); + + it('handles negative numbers', () => { + expect( + bnToHex(new BN(-1234), { isNegative: true }) + ).toBe('0xfb2e'); + }); + + it('handles negative numbers (with bitLength)', () => { + expect( + bnToHex(new BN(-1234), { bitLength: 32, isNegative: true }) + ).toBe('0xfffffb2e'); + }); + + it('handles backwards compatibility', () => { + expect( + bnToHex(new BN(1234), 32) + ).toBe( + bnToHex(new BN(1234), { bitLength: 32 }) + ); + }); +}); diff --git a/packages/util/src/bn/toHex.ts b/packages/util/src/bn/toHex.ts new file mode 100644 index 0000000000..0b610299f0 --- /dev/null +++ b/packages/util/src/bn/toHex.ts @@ -0,0 +1,49 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import isNumber from '../is/number'; +import bnToU8a from './toU8a'; +import { ToBnOptions } from '../types'; +import { u8aToHex } from '../u8a'; + +const ZERO_STR = '0x00'; + +interface Options extends ToBnOptions { + bitLength?: number; +} + +/** + * @name bnToHex + * @summary Creates a hex value from a BN.js bignumber object. + * @description + * `null` inputs returns a `0x` result, BN values return the actual value as a `0x` prefixed hex value. Anything that is not a BN object throws an error. With `bitLength` set, it fixes the number to the specified length. + * @example + *
+ * + * ```javascript + * import BN from 'bn.js'; + * import { bnToHex } from '@plugnet/util'; + * + * bnToHex(new BN(0x123456)); // => '0x123456' + * ``` + */ +export default function bnToHex ( + value?: BN | number | null, + options: number | Options = { bitLength: -1, isLe: false, isNegative: false } +): string { + if (!value) { + return ZERO_STR; + } + + const _options = { + isLe: false, + isNegative: false, + // Backwards-compatibility + ...(isNumber(options) ? { bitLength: options } : options) + }; + + return u8aToHex(bnToU8a(value, _options)); +} diff --git a/packages/util/src/bn/toU8a.spec.ts b/packages/util/src/bn/toU8a.spec.ts new file mode 100644 index 0000000000..998479ef55 --- /dev/null +++ b/packages/util/src/bn/toU8a.spec.ts @@ -0,0 +1,65 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import { bnToU8a } from '.'; + +describe('bnToU8a', () => { + it('converts null values to 0x00', () => { + expect( + bnToU8a(null, -1, false) + ).toEqual(new Uint8Array([])); + }); + + it('converts null values to 0x00000000 (bitLength)', () => { + expect( + bnToU8a(null, 32, false) + ).toEqual(new Uint8Array([0, 0, 0, 0])); + }); + + it('converts BN values to a prefixed hex representation', () => { + expect( + bnToU8a(new BN(0x123456), -1, false) + ).toEqual(new Uint8Array([0x12, 0x34, 0x56])); + }); + + it('converts BN values to a prefixed hex representation (bitLength)', () => { + expect( + bnToU8a(new BN(0x123456), 32, false) + ).toEqual(new Uint8Array([0x00, 0x12, 0x34, 0x56])); + }); + + it('converts using little endian (as set)', () => { + expect( + bnToU8a(new BN(0x123456), 32, true) + ).toEqual(new Uint8Array([0x56, 0x34, 0x12, 0x00])); + }); + + it('converts negative numbers', () => { + expect( + bnToU8a(new BN(-1234), { isNegative: true }) + ).toEqual(new Uint8Array([46, 251])); + }); + + it('converts negative numbers (BE)', () => { + expect( + bnToU8a(new BN(-1234), { isNegative: true, isLe: false }) + ).toEqual(new Uint8Array([251, 46])); + }); + + it('converts negative numbers (bitLength)', () => { + expect( + bnToU8a(new BN(-1234), { isNegative: true, bitLength: 32 }) + ).toEqual(new Uint8Array([46, 251, 255, 255])); + }); + + it('handles backwards compatibility', () => { + expect( + bnToU8a(new BN(1234), 32, false) + ).toEqual( + bnToU8a(new BN(1234), { bitLength: 32, isLe: false }) + ); + }); +}); diff --git a/packages/util/src/bn/toU8a.ts b/packages/util/src/bn/toU8a.ts new file mode 100644 index 0000000000..8bf9612995 --- /dev/null +++ b/packages/util/src/bn/toU8a.ts @@ -0,0 +1,63 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import isNumber from '../is/number'; +import bnToBn from './toBn'; +import { ToBnOptions } from '../types'; + +interface Options extends ToBnOptions { + bitLength?: number; +} + +/** + * @name bnToU8a + * @summary Creates a Uint8Array object from a BN. + * @description + * `null`/`undefined`/`NaN` inputs returns an empty `Uint8Array` result. `BN` input values return the actual bytes value converted to a `Uint8Array`. Optionally convert using little-endian format if `isLE` is set. + * @example + *
+ * + * ```javascript + * import { bnToU8a } from '@plugnet/util'; + * + * bnToU8a(new BN(0x1234)); // => [0x12, 0x34] + * ``` + */ +export default function bnToU8a (value: BN | number | null, options?: Options): Uint8Array; +export default function bnToU8a (value: BN | number | null, bitLength?: number, isLe?: boolean): Uint8Array; +export default function bnToU8a ( + value: BN | number | null, + arg1: number | Options = { bitLength: -1, isLe: true, isNegative: false }, + arg2?: boolean +): Uint8Array { + const _options: Options = { + isLe: true, + isNegative: false, + bitLength: -1, + ...isNumber(arg1) ? { bitLength: arg1, isLe: arg2 } : arg1 + }; + + const valueBn = bnToBn(value); + let byteLength = _options.bitLength === -1 + ? Math.ceil(valueBn.bitLength() / 8) + : Math.ceil(_options.bitLength! / 8); + + if (!value) { + return _options.bitLength === -1 + ? new Uint8Array([]) + : new Uint8Array(byteLength); + } + + const output = new Uint8Array(byteLength); + const bn = _options.isNegative ? valueBn.toTwos(byteLength * 8) : valueBn; + + output.set( + bn.toArray(_options.isLe ? 'le' : 'be', byteLength), + 0 + ); + + return output; +} diff --git a/packages/util/src/buffer/index.ts b/packages/util/src/buffer/index.ts new file mode 100644 index 0000000000..69de6a88d5 --- /dev/null +++ b/packages/util/src/buffer/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods to convert to and from `Buffer` objects + */ + +export { default as bufferToU8a } from './toU8a'; diff --git a/packages/util/src/buffer/toU8a.spec.ts b/packages/util/src/buffer/toU8a.spec.ts new file mode 100644 index 0000000000..1f6f991e1b --- /dev/null +++ b/packages/util/src/buffer/toU8a.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { bufferToU8a } from '.'; + +describe('bufferToU8a', () => { + it('returns an empty buffer when null provided', () => { + expect( + bufferToU8a(null) + ).toEqual(new Uint8Array([])); + }); + + it('returns a Uint8Buffer with the correct values', () => { + expect( + bufferToU8a(Buffer.from([128, 0, 10])) + ).toEqual(new Uint8Array([128, 0, 10])); + }); +}); diff --git a/packages/util/src/buffer/toU8a.ts b/packages/util/src/buffer/toU8a.ts new file mode 100644 index 0000000000..494fcc3f59 --- /dev/null +++ b/packages/util/src/buffer/toU8a.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name bufferToU8a + * @summary Creates a Uint8Array value from a Buffer object. + * @description + * `null` inputs returns an empty result, `Buffer` values return the actual value as a `Uint8Array`. Anything that is not a `Buffer` object throws an error. + * @example + *
+ * + * ```javascript + * import { bufferToU8a } from '@plugnet/util'; + * + * bufferToU8a(Buffer.from([1, 2, 3])); + * ``` + */ +export default function bufferToU8a (buffer?: Buffer | number[] | null): Uint8Array { + if (!buffer) { + return new Uint8Array([]); + } + + return new Uint8Array(buffer); +} diff --git a/packages/util/src/compact/addLength.spec.ts b/packages/util/src/compact/addLength.spec.ts new file mode 100644 index 0000000000..2d3f5ebd4a --- /dev/null +++ b/packages/util/src/compact/addLength.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import compactAddLength from './addLength'; + +describe('compactAddLength', () => { + it('correctly adds the length prefix', () => { + expect( + compactAddLength(Uint8Array.from([12, 13])) + ).toEqual(Uint8Array.from([2 << 2, 12, 13])); + }); +}); diff --git a/packages/util/src/compact/addLength.ts b/packages/util/src/compact/addLength.ts new file mode 100644 index 0000000000..1bba4d07bd --- /dev/null +++ b/packages/util/src/compact/addLength.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aConcat } from '../u8a'; +import compactToU8a from './toU8a'; + +/** + * @name compactAddLength + * @description Adds a length prefix to the input value + * @example + *
+ * + * ```javascript + * import { compactAddLength } from '@plugnet/util'; + * + * console.log(compactAddLength(new Uint8Array([0xde, 0xad, 0xbe, 0xef]))); // Uint8Array([4 << 2, 0xde, 0xad, 0xbe, 0xef]) + * ``` + */ +export default function compactAddLength (input: Uint8Array): Uint8Array { + return u8aConcat( + compactToU8a(input.length), + input + ); +} diff --git a/packages/util/src/compact/defaults.ts b/packages/util/src/compact/defaults.ts new file mode 100644 index 0000000000..00c4df0e64 --- /dev/null +++ b/packages/util/src/compact/defaults.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BitLength } from './types'; + +export const DEFAULT_BITLENGTH: BitLength = 32; diff --git a/packages/util/src/compact/fromU8a.spec.ts b/packages/util/src/compact/fromU8a.spec.ts new file mode 100644 index 0000000000..52d264252e --- /dev/null +++ b/packages/util/src/compact/fromU8a.spec.ts @@ -0,0 +1,48 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import hexToU8a from '../hex/toU8a'; +import compactFromU8a from './fromU8a'; + +describe('compactFromU8a', () => { + it('decoded u8 value', () => { + expect( + compactFromU8a(new Uint8Array([0b11111100])) + ).toEqual([1, new BN(63)]); + }); + + it('decodes from same u16 encoded value', () => { + expect( + compactFromU8a(new Uint8Array([0b11111101, 0b00000111]), 32) + ).toEqual([2, new BN(511)]); + }); + + it('decodes from same u32 encoded value (short)', () => { + expect( + compactFromU8a(new Uint8Array([254, 255, 3, 0]), 32) + ).toEqual([4, new BN(0xffff)]); + }); + + it('decodes from same u32 encoded value (full)', () => { + expect( + compactFromU8a(new Uint8Array([3, 249, 255, 255, 255]), 32) + ).toEqual([5, new BN(0xfffffff9)]); + }); + + it('decodes from same u32 as u64 encoded value (full, default)', () => { + expect( + compactFromU8a(new Uint8Array([3 + ((4 - 4) << 2), 249, 255, 255, 255]), 64) + ).toEqual([5, new BN(0xfffffff9)]); + }); + + it('decodes an actual value', () => { + expect( + compactFromU8a( + hexToU8a('0x0b00407a10f35a') + ) + ).toEqual([7, new BN('5af3107a4000', 16)]); + }); +}); diff --git a/packages/util/src/compact/fromU8a.ts b/packages/util/src/compact/fromU8a.ts new file mode 100644 index 0000000000..73c69cd22c --- /dev/null +++ b/packages/util/src/compact/fromU8a.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BitLength } from './types'; + +import BN from 'bn.js'; + +import { u8aToBn, u8aToU8a } from '../u8a'; +import { DEFAULT_BITLENGTH } from './defaults'; + +/** + * @name compactFromU8a + * @description Retrievs the offset and encoded length from a compact-prefixed value + * @example + *
+ * + * ```javascript + * import { compactFromU8a } from '@plugnet/util'; + * + * const [offset, length] = compactFromU8a(new Uint8Array([254, 255, 3, 0]), 32)); + * + * console.log('value offset=', offset, 'length=', length); // 4, 0xffff + * ``` + */ +export default function compactFromU8a (_input: Uint8Array | string, bitLength: BitLength = DEFAULT_BITLENGTH): [number, BN] { + const input = u8aToU8a(_input); + const flag = input[0] & 0b11; + + if (flag === 0b00) { + return [1, new BN(input[0]).shrn(2)]; + } else if (flag === 0b01) { + return [2, u8aToBn(input.slice(0, 2), true).shrn(2)]; + } else if (flag === 0b10) { + return [4, u8aToBn(input.slice(0, 4), true).shrn(2)]; + } + + const length = new BN(input[0]) + .shrn(2) // clear flag + .addn(4) // add 4 for base length + .toNumber(); + const offset = 1 + length; + + return [offset, u8aToBn(input.subarray(1, offset), true)]; +} diff --git a/packages/util/src/compact/index.ts b/packages/util/src/compact/index.ts new file mode 100644 index 0000000000..6f56f95cd8 --- /dev/null +++ b/packages/util/src/compact/index.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @description + * Encoding and decoding of parity-codec compact numbers. The codec is created + * to take up the least amount of space for a specific number. It performs the + * same function as Length, however differs in that it uses a variable number of + * bytes to do the actual encoding. From the Rust implementation for compact + * encoding: + * + * 0b00 00 00 00 / 00 00 00 00 / 00 00 00 00 / 00 00 00 00 + * (0 ... 2**6 - 1) (u8) + * xx xx xx 00 + * (2**6 ... 2**14 - 1) (u8, u16) low LH high + * yL yL yL 01 / yH yH yH yL + * (2**14 ... 2**30 - 1) (u16, u32) low LMMH high + * zL zL zL 10 / zM zM zM zL / zM zM zM zM / zH zH zH zM + * (2**30 ... 2**536 - 1) (u32, u64, u128, U256, U512, U520) straight LE-encoded + * nn nn nn 11 [ / zz zz zz zz ]{4 + n} + * + * Note: we use *LOW BITS* of the LSB in LE encoding to encode the 2 bit key. + */ + +export { default as compactAddLength } from './addLength'; +export { default as compactStripLength } from './stripLength'; +export { default as compactFromU8a } from './fromU8a'; +export { default as compactToU8a } from './toU8a'; diff --git a/packages/util/src/compact/stripLength.spec.ts b/packages/util/src/compact/stripLength.spec.ts new file mode 100644 index 0000000000..d00c8ed7f7 --- /dev/null +++ b/packages/util/src/compact/stripLength.spec.ts @@ -0,0 +1,16 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import compactStripLength from './stripLength'; + +describe('compactStripLength', () => { + it('correctly removes the length prefix', () => { + expect( + compactStripLength(Uint8Array.from([2 << 2, 12, 13])) + ).toEqual([ + 3, + Uint8Array.from([12, 13]) + ]); + }); +}); diff --git a/packages/util/src/compact/stripLength.ts b/packages/util/src/compact/stripLength.ts new file mode 100644 index 0000000000..85bfeb0ebb --- /dev/null +++ b/packages/util/src/compact/stripLength.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { BitLength } from './types'; + +import { DEFAULT_BITLENGTH } from './defaults'; +import compactFromU8a from './fromU8a'; + +/** + * @name compactStripLength + * @description Removes the length prefix, returning both the total length (including the value + compact encoding) and the decoded value with the correct length + * @example + *
+ * + * ```javascript + * import { compactStripLength } from '@plugnet/util'; + * + * console.log(compactStripLength(new Uint8Array([2 << 2, 0xde, 0xad]))); // [2, Uint8Array[0xde, 0xad]] + * ``` + */ +export default function compactStripLength (input: Uint8Array, bitLength: BitLength = DEFAULT_BITLENGTH): [number, Uint8Array] { + const [offset, length] = compactFromU8a(input, bitLength); + const total = offset + length.toNumber(); + + return [ + total, + input.subarray(offset, total) + ]; +} diff --git a/packages/util/src/compact/toU8a.spec.ts b/packages/util/src/compact/toU8a.spec.ts new file mode 100644 index 0000000000..3e9b26f2a3 --- /dev/null +++ b/packages/util/src/compact/toU8a.spec.ts @@ -0,0 +1,102 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import compactToU8a from './toU8a'; + +describe('encode', () => { + it('encodes short u8', () => { + expect( + compactToU8a(18) + ).toEqual( + new Uint8Array([18 << 2]) + ); + }); + + it('encodes max u8 values', () => { + expect( + compactToU8a(new BN(63)) + ).toEqual( + new Uint8Array([0b11111100]) + ); + }); + + it('encodes basic u16 value', () => { + expect( + compactToU8a(511) + ).toEqual( + new Uint8Array([0b11111101, 0b00000111]) + ); + }); + + it('encodes basic u16 (not at edge)', () => { + expect( + compactToU8a(111) + ).toEqual( + new Uint8Array([0xbd, 0x01]) + ); + }); + + it('encodes basic u32 values (short)', () => { + expect( + compactToU8a(0xffff) + ).toEqual( + new Uint8Array([254, 255, 3, 0]) + ); + }); + + it('encodes basic u32 values (full)', () => { + expect( + compactToU8a(0xfffffff9) + ).toEqual( + new Uint8Array([3 + ((4 - 4) << 2), 249, 255, 255, 255]) + ); + }); + + it('encodes a large value', () => { + expect( + compactToU8a( + new BN('00005af3107a4000', 16) + ) + ).toEqual( + new Uint8Array([3 + ((6 - 4) << 2), 0x00, 0x40, 0x7a, 0x10, 0xf3, 0x5a]) + ); + }); + + describe('from Rust', () => { + // Copied from https://github.com/paritytech/parity-codec/blob/master/src/codec.rs + const testCases = [ + { value: new BN('0'), expected: '00' }, + { value: new BN('63'), expected: 'fc' }, + { value: new BN('64'), expected: '01 01' }, + { value: new BN('16383'), expected: 'fd ff' }, + { value: new BN('16384'), expected: '02 00 01 00' }, + { value: new BN('1073741823'), expected: 'fe ff ff ff' }, + { value: new BN('1073741824'), expected: '03 00 00 00 40' }, + { value: new BN(`${1}${'0'.repeat(32)}`, 2).subn(1), expected: '03 ff ff ff ff' }, + { value: new BN(`${1}${'0'.repeat(32)}`, 2), expected: '07 00 00 00 00 01' }, + { value: new BN(`${1}${'0'.repeat(40)}`, 2), expected: '0b 00 00 00 00 00 01' }, + { value: new BN(`${1}${'0'.repeat(48)}`, 2), expected: '0f 00 00 00 00 00 00 01' }, + { value: new BN(`${1}${'0'.repeat(56)}`, 2).subn(1), expected: '0f ff ff ff ff ff ff ff' }, + { value: new BN(`${1}${'0'.repeat(56)}`, 2), expected: '13 00 00 00 00 00 00 00 01' }, + { value: new BN(`${1}${'0'.repeat(64)}`, 2).subn(1), expected: '13 ff ff ff ff ff ff ff ff' } + ]; + + function testEncode (value: BN, expected: string) { + it(`encodes ${value.toString()}`, () => { + expect( + compactToU8a(value) + ).toEqual( + Uint8Array.from( + expected.split(' ').map(s => parseInt(s, 16)) + ) + ); + }); + } + + testCases.forEach((({ value, expected }) => testEncode(value, expected))); + }); + +}); diff --git a/packages/util/src/compact/toU8a.ts b/packages/util/src/compact/toU8a.ts new file mode 100644 index 0000000000..4fd564a6db --- /dev/null +++ b/packages/util/src/compact/toU8a.ts @@ -0,0 +1,55 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import assert from '../assert'; +import { bnToBn, bnToU8a } from '../bn'; +import { u8aConcat } from '../u8a'; + +const MAX_U8 = new BN(2).pow(new BN(8 - 2)).subn(1); +const MAX_U16 = new BN(2).pow(new BN(16 - 2)).subn(1); +const MAX_U32 = new BN(2).pow(new BN(32 - 2)).subn(1); + +/** + * @name compactToU8a + * @description Encodes a number into a compact representation + * @example + *
+ * + * ```javascript + * import { compactToU8a } from '@plugnet/util'; + * + * console.log(compactToU8a(511, 32)); // Uint8Array([0b11111101, 0b00000111]) + * ``` + */ +export default function compactToU8a (_value: BN | number): Uint8Array { + const value = bnToBn(_value); + + if (value.lte(MAX_U8)) { + return new Uint8Array([value.toNumber() << 2]); + } else if (value.lte(MAX_U16)) { + return bnToU8a(value.shln(2).addn(0b01), 16, true); + } else if (value.lte(MAX_U32)) { + return bnToU8a(value.shln(2).addn(0b10), 32, true); + } + + const u8a = bnToU8a(value); + let length = u8a.length; + + // adjust to the minimum number of bytes + while (u8a[length - 1] === 0) { + length--; + } + + assert(length >= 4, 'Previous tests match anyting less than 2^30; qed'); + + return u8aConcat( + new Uint8Array([ + // substract 4 as minimum (also catered for in decoding) + ((length - 4) << 2) + 0b11 + ]), + u8a.subarray(0, length) + ); +} diff --git a/packages/util/src/compact/types.ts b/packages/util/src/compact/types.ts new file mode 100644 index 0000000000..8d232ed9ca --- /dev/null +++ b/packages/util/src/compact/types.ts @@ -0,0 +1,5 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type BitLength = 8 | 16 | 32 | 64 | 128 | 256; diff --git a/packages/util/src/ext/error.spec.ts b/packages/util/src/ext/error.spec.ts new file mode 100644 index 0000000000..e2402025fa --- /dev/null +++ b/packages/util/src/ext/error.spec.ts @@ -0,0 +1,99 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isError from '../is/error'; +import { ExtError } from '.'; + +describe('ExtError', () => { + describe('constructor', () => { + it('constructs an Error that is still an Error', () => { + expect( + isError( + new ExtError() + ) + ).toEqual(true); + }); + }); + + describe('static', () => { + it('exposes the .CODES as a static', () => { + expect( + Object.keys(ExtError.CODES) + ).not.toEqual(0); + }); + }); + + describe('constructor properties', () => { + it('sets the .message property', () => { + expect( + new ExtError('test message').message + ).toEqual('test message'); + }); + + it("sets the .message to '' when not set", () => { + expect( + new ExtError().message + ).toEqual(''); + }); + + it('sets the .code property', () => { + expect( + new ExtError('test message', 1234).code + ).toEqual(1234); + }); + + it('sets the .code to UKNOWN when not set', () => { + expect( + new ExtError('test message').code + ).toEqual(ExtError.CODES.UNKNOWN); + }); + + it('sets the .data property', () => { + const data = { some: { value: 'here' } }; + + expect( + new ExtError('test message', 1234, data).data + ).toEqual(data); + }); + }); + + describe('stack traces', () => { + let captureStackTrace: any; + + beforeEach(() => { + captureStackTrace = Error.captureStackTrace; + + Error.captureStackTrace = function (error) { + Object.defineProperty(error, 'stack', { + configurable: true, + get: function getStack () { + const value = 'some stack returned'; + + Object.defineProperty(this, 'stack', { value }); + + return value; + } + }); + }; + }); + + afterEach(() => { + Error.captureStackTrace = captureStackTrace; + }); + + it('captures via captureStackTrace when available', () => { + expect( + new ExtError().stack + ).toEqual('some stack returned'); + }); + + it('captures via stack when captureStackTrace not available', () => { + Error.captureStackTrace = null as any; // grrr + + expect( + new ExtError().stack.length + ).not.toEqual(0); + }); + }); +}); diff --git a/packages/util/src/ext/error.ts b/packages/util/src/ext/error.ts new file mode 100644 index 0000000000..75da047725 --- /dev/null +++ b/packages/util/src/ext/error.ts @@ -0,0 +1,66 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { ExtErrorInterface } from '../types'; + +import isFunction from '../is/function'; + +const UNKNOWN = -99999; + +function extend (that: any, name: string, value: any): void { + Object.defineProperty(that, name, { + configurable: true, + enumerable: false, + value + }); +} + +/** + * @name ExtError + * @summary Extension to the basic JS Error. + * @description + * The built-in JavaScript Error class is extended by adding a code to allow for Error categorization. In addition to the normal `stack`, `message`, the numeric `code` and `data` (any types) parameters are available on the object. + * @example + *
+ * + * ```javascript + * const { ExtError } from '@plugnet/util'); + * + * throw new ExtError('some message', ExtError.CODES.METHOD_NOT_FOUND); // => error.code = -32601 + * ``` + */ +export default class ExtError extends Error implements ExtErrorInterface { + // @ts-ignore we are assigning it via extend + code: number; + // @ts-ignore we are assigning it via extend + data: any; + // @ts-ignore we are assigning it via extend + message: string; + // @ts-ignore we are assigning it via extend + name: string; + // @ts-ignore we are assigning it via extend + stack: string; + + constructor (message: string = '', code: number = UNKNOWN, data?: any) { + super(); + + extend(this, 'message', String(message)); + extend(this, 'name', this.constructor.name); + extend(this, 'data', data); + extend(this, 'code', code); + + if (isFunction(Error.captureStackTrace)) { + Error.captureStackTrace(this, this.constructor); + } else { + extend(this, 'stack', (new Error(message)).stack); + } + } + + static CODES = { + ASSERT: -90009, + UNKNOWN, + INVALID_JSONRPC: -99998, + METHOD_NOT_FOUND: -32601 // Rust client + }; +} diff --git a/packages/util/src/ext/index.ts b/packages/util/src/ext/index.ts new file mode 100644 index 0000000000..f73ae68c8f --- /dev/null +++ b/packages/util/src/ext/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Extensions to basic classes + */ + +export { default as ExtError } from './error'; diff --git a/packages/util/src/format/formatBalance.spec.ts b/packages/util/src/format/formatBalance.spec.ts new file mode 100644 index 0000000000..14a0a3dcbb --- /dev/null +++ b/packages/util/src/format/formatBalance.spec.ts @@ -0,0 +1,121 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import formatBalance from './formatBalance'; + +describe('formatBalance', () => { + const TESTVAL = new BN('123456789000'); + + it('formats empty to 0', () => { + expect(formatBalance()).toEqual('0'); + expect(formatBalance('0')).toEqual('0'); + }); + + it('formats 123,456,789,000 (decimals=15)', () => { + expect( + formatBalance(TESTVAL, true, 15) + ).toEqual('123.456µ Unit'); + }); + + it('formats 123,456,789,000 (decimals=12)', () => { + expect( + formatBalance(TESTVAL, true, 12) + ).toEqual('123.456m Unit'); + }); + + it('formats 123,456,789,000 (decimals=12, no SI)', () => { + expect( + formatBalance(TESTVAL, false, 12) + ).toEqual('123.456'); + }); + + it('formats 123,456,789,000 (decimals=9)', () => { + expect( + formatBalance(TESTVAL, true, 9) + ).toEqual('123.456 Unit'); + }); + + it('formats 123,456,789,000 (decimals=6)', () => { + expect( + formatBalance(TESTVAL, true, 6) + ).toEqual('123.456k Unit'); + }); + + it('formats 123,456,789,000 * 10 (decimals=12)', () => { + expect( + formatBalance(TESTVAL.muln(10), true, 12) + ).toEqual('1.234 Unit'); + }); + + it('formats 123,456,789,000 * 100 (decimals=12)', () => { + expect( + formatBalance(TESTVAL.muln(100), true, 12) + ).toEqual('12.345 Unit'); + }); + + it('formats 123,456,789,000 * 1000 (decimals=12)', () => { + expect( + formatBalance(TESTVAL.muln(1000), true, 12) + ).toEqual('123.456 Unit'); + }); + + describe('findSi', () => { + it('finds the SI value', () => { + expect( + formatBalance.findSi('k') + ).toEqual({ power: 3, value: 'k', text: 'Kilo' }); + }); + + it('returns default on not found', () => { + expect( + formatBalance.findSi('blah') + ).toEqual({ power: 0, value: '-', text: 'Unit' }); + }); + }); + + describe('defaults', () => { + it('returns defaults', () => { + expect(formatBalance.getDefaults()).toEqual({ + decimals: 0, + unit: 'Unit' + }); + }); + + it('formats 123,456,789,000 (defaultDecimals=12)', () => { + formatBalance.setDefaults({ decimals: 12 }); + + expect( + formatBalance(TESTVAL) + ).toEqual('123.456m Unit'); + }); + + it('formats 123,456,789,000 (defaultUnit=TEST)', () => { + formatBalance.setDefaults({ unit: 'TEST' }); + + expect( + formatBalance(TESTVAL) + ).toEqual('123.456m TEST'); + }); + }); + + it('returns options for dropdown', () => { + formatBalance.setDefaults({ decimals: 0, unit: 'TEST' }); + + expect( + formatBalance.getOptions() + ).toEqual([ + { power: 0, value: '-', text: 'TEST' }, + { power: 3, value: 'k', text: 'Kilo' }, + { power: 6, value: 'M', text: 'Mega' }, + { power: 9, value: 'G', text: 'Giga' }, + { power: 12, value: 'T', text: 'Tera' }, + { power: 15, value: 'P', text: 'Peta' }, + { power: 18, value: 'E', text: 'Exa' }, + { power: 21, value: 'Z', text: 'Zeta' }, + { power: 24, value: 'Y', text: 'Yotta' } + ]); + }); +}); diff --git a/packages/util/src/format/formatBalance.ts b/packages/util/src/format/formatBalance.ts new file mode 100644 index 0000000000..7b4b4d4979 --- /dev/null +++ b/packages/util/src/format/formatBalance.ts @@ -0,0 +1,87 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import isUndefined from '../is/undefined'; +import { SI, SI_MID, SiDef, calcSi, findSi } from './si'; +import formatDecimal from './formatDecimal'; + +type Defaults = { + decimals: number, + unit: string +}; + +interface BalanceFormatter { + (input?: number | string | BN, withSi?: boolean, decimals?: number): string; + calcSi (text: string, decimals?: number): SiDef; + findSi (type: string): SiDef; + getDefaults (): Defaults; + getOptions (decimals?: number): Array; + setDefaults (defaults: Partial): void; +} + +const DEFAULT_DECIMALS = 0; +const DEFAULT_UNIT = SI[SI_MID].text; + +let defaultDecimals = DEFAULT_DECIMALS; +let defaultUnit = DEFAULT_UNIT; + +// Formats a string/number with . notation +function _formatBalance (input?: number | string | BN, withSi: boolean = true, decimals: number = defaultDecimals): string { + const text = (input || '').toString(); + + if (text.length === 0 || text === '0') { + return '0'; + } + + // NOTE We start at midpoint (8) minus 1 - this means that values display as + // 123.456 instead of 0.123k (so always 6 relevant). Additionally we us ceil + // so there are at most 3 decimal before the decimal seperator + const si = calcSi(text, decimals); + const mid = text.length - (decimals + si.power); + const prefix = text.substr(0, mid); + const postfix = `${text.substr(mid)}000`.substr(0, 3); + const units = withSi + ? ( + si.value === '-' + ? ` ${si.text}` + : `${si.value} ${SI[SI_MID].text}` + ) + : ''; + + return `${formatDecimal(prefix || '0')}.${postfix}${units}`; +} + +const formatBalance = _formatBalance as BalanceFormatter; + +formatBalance.calcSi = (text: string, decimals: number = defaultDecimals): SiDef => + calcSi(text, decimals); +formatBalance.findSi = findSi; + +formatBalance.getDefaults = (): Defaults => { + return { + decimals: defaultDecimals, + unit: defaultUnit + }; +}; + +// get allowable options to display in a dropdown +formatBalance.getOptions = (decimals: number = defaultDecimals): Array => { + return SI.filter(({ power }) => + power < 0 + ? (decimals + power) >= 0 + : true + ); +}; + +// Sets the default decimals to use for formatting (ui-wide) +formatBalance.setDefaults = ({ decimals, unit }: Partial): void => { + defaultDecimals = isUndefined(decimals) ? defaultDecimals : decimals; + defaultUnit = isUndefined(unit) ? defaultUnit : unit; + + SI[SI_MID].text = defaultUnit; +}; + +export default formatBalance; diff --git a/packages/util/src/format/formatDecimal.spec.ts b/packages/util/src/format/formatDecimal.spec.ts new file mode 100644 index 0000000000..49cbb207c7 --- /dev/null +++ b/packages/util/src/format/formatDecimal.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import formatDecimal from './formatDecimal'; + +describe('formatDecimal', () => { + it('formats decimals in number groupings', () => { + expect(formatDecimal('12345')).toEqual('12,345'); + }); + + it('formats decimal-only in number groupings', () => { + expect(formatDecimal('test6789')).toEqual('6,789'); + }); + + it('returns input for non-decimal', () => { + expect(formatDecimal('test')).toEqual('test'); + }); +}); diff --git a/packages/util/src/format/formatDecimal.ts b/packages/util/src/format/formatDecimal.ts new file mode 100644 index 0000000000..097c21462c --- /dev/null +++ b/packages/util/src/format/formatDecimal.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g'); + +export default function formatDecimal (value: string): string { + const matched = value.match(NUMBER_REGEX); + + return matched + ? matched.join(',') + : value; +} diff --git a/packages/util/src/format/formatElapsed.spec.ts b/packages/util/src/format/formatElapsed.spec.ts new file mode 100644 index 0000000000..8ec261b750 --- /dev/null +++ b/packages/util/src/format/formatElapsed.spec.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Compact } from './types'; + +import BN from 'bn.js'; + +import formatElapsed from './formatElapsed'; + +describe('formatElapsed', () => { + const start = 12345678; + const now = new Date(12345678); + + it('formats a Date', () => { + expect( + formatElapsed(now, new Date(start + 9700)) + ).toEqual('9.7s'); + }); + + it('formats a BN', () => { + expect( + formatElapsed(now, new BN(start + 42700)) + ).toEqual('42s'); + }); + + it('formats a Compact', () => { + expect( + formatElapsed(now, { unwrap: () => new BN(start + (5.3 * 60000)) } as Compact) + ).toEqual('5m'); + }); + + it('formats a number', () => { + expect( + formatElapsed(now, start + (42 * 60 * 60000)) + ).toEqual('42h'); + }); + + it('formats defaults', () => { + expect( + formatElapsed() + ).toEqual('0.0s'); + }); +}); diff --git a/packages/util/src/format/formatElapsed.ts b/packages/util/src/format/formatElapsed.ts new file mode 100644 index 0000000000..18ff900f22 --- /dev/null +++ b/packages/util/src/format/formatElapsed.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/ui-util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Compact } from './types'; + +import BN from 'bn.js'; + +function getValue (value?: BN | Compact | Date | number | null): number { + if (value) { + if ((value as any).unwrap) { + return getValue((value as Compact).unwrap()); + } else if (value instanceof Date) { + return getValue(value.getTime()); + } else if (value instanceof BN) { + return getValue(value.toNumber()); + } + } + + return (value as number) || 0; +} + +export default function formatElapsed (now?: Date | null, value?: BN | Compact | Date | number | null): string { + const tsNow = (now && now.getTime()) || 0; + const tsValue = getValue(value); + let display = '0.0s'; + + if (tsNow && tsValue) { + const elapsed = Math.max(Math.abs(tsNow - tsValue), 0) / 1000; + + if (elapsed < 15) { + display = `${elapsed.toFixed(1)}s`; + } else if (elapsed < 60) { + display = `${elapsed | 0}s`; + } else if (elapsed < 3600) { + display = `${elapsed / 60 | 0}m`; + } else { + display = `${elapsed / 3600 | 0}h`; + } + } + + return display; +} diff --git a/packages/util/src/format/formatNumber.ts b/packages/util/src/format/formatNumber.ts new file mode 100644 index 0000000000..7100219036 --- /dev/null +++ b/packages/util/src/format/formatNumber.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Compact } from './types'; + +import BN from 'bn.js'; + +import bnToBn from '../bn/toBn'; +import formatDecimal from './formatDecimal'; + +export default function formatNumber (_value?: Compact | BN | number | null): string { + if (!_value) { + return '0'; + } + + const value = (_value as any).toBn + ? (_value as Compact).toBn() + : bnToBn(_value as BN); + + return formatDecimal(value.toString()); +} diff --git a/packages/util/src/format/index.ts b/packages/util/src/format/index.ts new file mode 100644 index 0000000000..222e553f8c --- /dev/null +++ b/packages/util/src/format/index.ts @@ -0,0 +1,9 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export { default as formatBalance } from './formatBalance'; +export { default as formatDecimal } from './formatDecimal'; +export { default as formatElapsed } from './formatElapsed'; +export { default as formatNumber } from './formatNumber'; +export { calcSi, findSi } from './si'; diff --git a/packages/util/src/format/si.ts b/packages/util/src/format/si.ts new file mode 100644 index 0000000000..0f0b4763f9 --- /dev/null +++ b/packages/util/src/format/si.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export type SiDef = { + power: number, + text: string, + value: string +}; + +export const SI_MID = 8; + +export const SI: Array = [ + { power: -24, value: 'y', text: 'yocto' }, + { power: -21, value: 'z', text: 'zepto' }, + { power: -18, value: 'a', text: 'atto' }, + { power: -15, value: 'f', text: 'femto' }, + { power: -12, value: 'p', text: 'pico' }, + { power: -9, value: 'n', text: 'nano' }, + { power: -6, value: 'µ', text: 'micro' }, + { power: -3, value: 'm', text: 'milli' }, + { power: 0, value: '-', text: 'Unit' }, // position 8 + { power: 3, value: 'k', text: 'Kilo' }, + { power: 6, value: 'M', text: 'Mega' }, + { power: 9, value: 'G', text: 'Giga' }, + { power: 12, value: 'T', text: 'Tera' }, + { power: 15, value: 'P', text: 'Peta' }, + { power: 18, value: 'E', text: 'Exa' }, + { power: 21, value: 'Z', text: 'Zeta' }, + { power: 24, value: 'Y', text: 'Yotta' } +]; + +export function calcSi (text: string, decimals: number): SiDef { + return SI[(SI_MID - 1) + Math.ceil((text.length - decimals) / 3)] || SI[SI.length - 1]; +} + +// Given a SI type (e.g. k, m, Y) find the SI definition +export function findSi (type: string): SiDef { + // use a loop here, better RN support (which doesn't have [].find) + for (let i = 0; i < SI.length; i++) { + if (SI[i].value === type) { + return SI[i]; + } + } + + return SI[SI_MID]; +} diff --git a/packages/util/src/format/types.ts b/packages/util/src/format/types.ts new file mode 100644 index 0000000000..3fc6b4a1ae --- /dev/null +++ b/packages/util/src/format/types.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +export interface Compact { + toBn: () => BN; + unwrap: () => BN | Date; +} diff --git a/packages/util/src/hex/addPrefix.spec.ts b/packages/util/src/hex/addPrefix.spec.ts new file mode 100644 index 0000000000..fda83d2a02 --- /dev/null +++ b/packages/util/src/hex/addPrefix.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexAddPrefix } from '.'; + +describe('hexAddPrefix', () => { + it('does not add when prefix is available', () => { + expect( + hexAddPrefix('0x0123') + ).toEqual('0x0123'); + }); + + it('adds the prefix when it is not available', () => { + expect( + hexAddPrefix('0123') + ).toEqual('0x0123'); + }); + + it('adds extra 0 when length % 2 === 1', () => { + expect( + hexAddPrefix('123') + ).toEqual('0x0123'); + }); + + it('returns null as 0x', () => { + expect( + hexAddPrefix(null) + ).toEqual('0x'); + }); +}); diff --git a/packages/util/src/hex/addPrefix.ts b/packages/util/src/hex/addPrefix.ts new file mode 100644 index 0000000000..43dbf78f2e --- /dev/null +++ b/packages/util/src/hex/addPrefix.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hasPrefix from './hasPrefix'; + +/** + * @name hexAddPrefix + * @summary Adds the `0x` prefix to string values. + * @description + * Returns a `0x` prefixed string from the input value. If the input is already prefixed, it is returned unchanged. + * @example + *
+ * + * ```javascript + * import { hexAddPrefix } from '@plugnet/util'; + * + * console.log('With prefix', hexAddPrefix('0a0b12')); // => 0x0a0b12 + * ``` + */ +export default function hexAddPrefix (value?: string | null): string { + if (value && hasPrefix(value)) { + return value; + } + + const prefix = value && value.length % 2 === 1 + ? '0' + : ''; + + return `0x${prefix}${value || ''}`; +} diff --git a/packages/util/src/hex/fixLength.spec.ts b/packages/util/src/hex/fixLength.spec.ts new file mode 100644 index 0000000000..93bc99aeb9 --- /dev/null +++ b/packages/util/src/hex/fixLength.spec.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexFixLength } from '.'; + +describe('hexFixLength', () => { + it('returns bitLength === -1 as-is', () => { + expect( + hexFixLength('0x12345678') + ).toEqual('0x12345678'); + }); + + it('returns bitLength === -1 as-is (adding missing 0)', () => { + expect( + hexFixLength('0x1234567') + ).toEqual('0x01234567'); + }); + + it('does not change when bitlength === length', () => { + expect( + hexFixLength('0x12345678', 32) + ).toEqual('0x12345678'); + }); + + it('trims values when bitLength > length', () => { + expect( + hexFixLength('0x12345678', 16) + ).toEqual('0x5678'); + }); + + it('returns as-is when bitLength < length', () => { + expect( + hexFixLength('0x1234', 32) + ).toEqual('0x1234'); + }); + + it('adds zeros when bitLength < length (withPadded)', () => { + expect( + hexFixLength('0x1234', 32, true) + ).toEqual('0x00001234'); + }); +}); diff --git a/packages/util/src/hex/fixLength.ts b/packages/util/src/hex/fixLength.ts new file mode 100644 index 0000000000..54763d62a6 --- /dev/null +++ b/packages/util/src/hex/fixLength.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hexAddPrefix from './addPrefix'; +import hexStripPrefix from './stripPrefix'; + +/** + * @name hexFixLength + * @summary Shifts a hex string to a specific bitLength + * @description + * Returns a `0x` prefixed string with the specified number of bits contained in the return value. (If bitLength is -1, length checking is not done). Values with more bits are trimmed to the specified length. Input values with less bits are returned as-is by default. When `withPadding` is set, shorter values are padded with `0`. + * @example + *
+ * + * ```javascript + * import { hexFixLength } from '@plugnet/util'; + * + * console.log('fixed', hexFixLength('0x12', 16)); // => 0x12 + * console.log('fixed', hexFixLength('0x12', 16, true)); // => 0x0012 + * console.log('fixed', hexFixLength('0x0012', 8)); // => 0x12 + * ``` + */ +export default function hexFixLength (value: string, bitLength: number = -1, withPadding: boolean = false): string { + const strLength = Math.ceil(bitLength / 4); + const hexLength = strLength + 2; + + if (bitLength === -1 || value.length === hexLength || (!withPadding && value.length < hexLength)) { + return hexAddPrefix( + hexStripPrefix(value) + ); + } + + if (value.length > hexLength) { + return hexAddPrefix( + hexStripPrefix(value).slice(-1 * strLength) + ); + } + + return hexAddPrefix( + `${'0'.repeat(strLength)}${hexStripPrefix(value)}`.slice(-1 * strLength) + ); +} diff --git a/packages/util/src/hex/hasPrefix.spec.ts b/packages/util/src/hex/hasPrefix.spec.ts new file mode 100644 index 0000000000..bb10a86a95 --- /dev/null +++ b/packages/util/src/hex/hasPrefix.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexHasPrefix } from '.'; + +describe('hexHasPrefix', () => { + it('returns true when hex prefix is found', () => { + expect( + hexHasPrefix('0x123') + ).toEqual(true); + }); + + it('returns false when no prefix attached', () => { + expect( + hexHasPrefix('123') + ).toEqual(false); + }); + + it('returns false when null value supplied', () => { + expect( + hexHasPrefix(null) + ).toEqual(false); + }); + + it('returns false when non-string value supplied', () => { + expect( + hexHasPrefix(false as any) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/hex/hasPrefix.ts b/packages/util/src/hex/hasPrefix.ts new file mode 100644 index 0000000000..ead8f2ae6b --- /dev/null +++ b/packages/util/src/hex/hasPrefix.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isHex from '../is/hex'; + +/** + * @name hexHasPrefix + * @summary Tests for the existence of a `0x` prefix. + * @description + * Checks for a valid hex input value and if the start matched `0x` + * @example + *
+ * + * ```javascript + * import { hexHasPrefix } from '@plugnet/util'; + * + * console.log('has prefix', hexHasPrefix('0x1234')); // => true + * ``` + */ +export default function hexHasPrefix (value?: string | null): boolean { + return !!(value && isHex(value, -1, true) && value.substr(0, 2) === '0x'); +} diff --git a/packages/util/src/hex/index.ts b/packages/util/src/hex/index.ts new file mode 100644 index 0000000000..acf2fe50fa --- /dev/null +++ b/packages/util/src/hex/index.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Internal utilities to create and test for hex values + */ + +export { default as hexAddPrefix } from './addPrefix'; +export { default as hexFixLength } from './fixLength'; +export { default as hexHasPrefix } from './hasPrefix'; +export { default as hexStripPrefix } from './stripPrefix'; +export { default as hexToBn } from './toBn'; +export { default as hexToNumber } from './toNumber'; +export { default as hexToU8a } from './toU8a'; diff --git a/packages/util/src/hex/stripPrefix.spec.ts b/packages/util/src/hex/stripPrefix.spec.ts new file mode 100644 index 0000000000..46aca5155e --- /dev/null +++ b/packages/util/src/hex/stripPrefix.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexStripPrefix } from '.'; + +describe('hexStripPrefix', () => { + it('returns an empty string when null value supplied', () => { + expect( + hexStripPrefix(null) + ).toEqual(''); + }); + + it('strips the prefix from hex strings', () => { + expect( + hexStripPrefix('0x1223') + ).toEqual('1223'); + }); + + it('returns un-prefixed hex as-is', () => { + expect( + hexStripPrefix('abcd1223') + ).toEqual('abcd1223'); + }); + + it('throws when invalid hex', () => { + expect( + () => hexStripPrefix('0x0x01ab') + ).toThrow(/Invalid hex/); + }); +}); diff --git a/packages/util/src/hex/stripPrefix.ts b/packages/util/src/hex/stripPrefix.ts new file mode 100644 index 0000000000..f0dfbb1b76 --- /dev/null +++ b/packages/util/src/hex/stripPrefix.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hasPrefix from './hasPrefix'; + +const UNPREFIX_HEX_REGEX = /^[a-fA-F0-9]+$/; + +/** + * @name hexStripPrefix + * @summary Strips any leading `0x` prefix. + * @description + * Tests for the existence of a `0x` prefix, and returns the value without the prefix. Un-prefixed values are returned as-is. + * @example + *
+ * + * ```javascript + * import { hexStripPrefix } from '@plugnet/util'; + * + * console.log('stripped', hexStripPrefix('0x1234')); // => 1234 + * ``` + */ +export default function hexStripPrefix (value?: string | null): string { + if (!value) { + return ''; + } + + if (hasPrefix(value)) { + return value.substr(2); + } + + if (UNPREFIX_HEX_REGEX.test(value)) { + return value; + } + + throw new Error(`Invalid hex ${value} passed to hexStripPrefix`); +} diff --git a/packages/util/src/hex/toBn.spec.ts b/packages/util/src/hex/toBn.spec.ts new file mode 100644 index 0000000000..41f4cead14 --- /dev/null +++ b/packages/util/src/hex/toBn.spec.ts @@ -0,0 +1,80 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToBn } from '.'; + +describe('hexToBn', () => { + it('converts prefixed hex values to BN', () => { + expect( + hexToBn(0x81).toString(16) + ).toBe('81'); + }); + + it('converts null values to BN(0)', () => { + expect( + hexToBn(null).toNumber() + ).toBe(0); + }); + + it('converts 0x values to BN(0)', () => { + expect( + hexToBn('0x').toNumber() + ).toBe(0); + }); + + it('should convert with Big Endian by default', () => { + expect( + hexToBn('0x0100').toNumber() + ).toBe(256); + }); + + it('converts 0x values to BN(0) (LE)', () => { + expect( + hexToBn('0x', { isLe: true }).toNumber() + ).toBe(0); + }); + + it('converts little-endian', () => { + expect( + hexToBn('0x4500000000000000', { isLe: true }).toNumber() + ).toBe(69); + }); + + it('handles negative numbers (LE)', () => { + expect( + hexToBn('0x2efb', { isLe: true, isNegative: true }).toNumber() + ).toBe(-1234); + }); + + it('handles negative numbers (BE)', () => { + expect( + hexToBn('0xfb2e', { isLe: false, isNegative: true }).toNumber() + ).toBe(-1234); + }); + + it('handles starting zeros correctly (BE)', () => { + expect( + hexToBn('0x0000000000000100', { isLe: false }).toNumber() + ).toBe(256); + }); + + it('handles starting zeros correctly (LE)', () => { + expect( + hexToBn('0x0001000000000000', { isLe: true }).toNumber() + ).toBe(256); + }); + + it('handles backwards compatibility', () => { + expect( + hexToBn('0x0001000000000000', false).eq( + hexToBn('0x0001000000000000', { isLe: false }) + ) + ).toBe(true); + expect( + hexToBn('0x0001000000000000', true).eq( + hexToBn('0x0001000000000000', { isLe: true }) + ) + ).toBe(true); + }); +}); diff --git a/packages/util/src/hex/toBn.ts b/packages/util/src/hex/toBn.ts new file mode 100644 index 0000000000..6a0c7c756c --- /dev/null +++ b/packages/util/src/hex/toBn.ts @@ -0,0 +1,59 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import isBoolean from '../is/boolean'; +import hexStripPrefix from './stripPrefix'; +import { ToBnOptions } from '../types'; + +function reverse (value: string): string { + return (value.match(new RegExp('.{1,2}', 'g')) || []) + .reverse() + .join(''); +} + +/** + * @name hexToBn + * @summary Creates a BN.js bignumber object from a hex string. + * @description + * `null` inputs returns a `BN(0)` result. Hex input values return the actual value converted to a BN. Anything that is not a hex string (including the `0x` prefix) throws an error. + * @param _value The value to convert + * @param _options Options to pass while converting + * @param _options.isLe Convert using Little Endian + * @param _options.isNegative Convert using two's complement + * @example + *
+ * + * ```javascript + * import { hexToBn } from '@plugnet/util'; + * + * hexToBn('0x123480001f'); // => BN(0x123480001f) + * ``` + */ +export default function hexToBn ( + value?: string | number | null, + options: ToBnOptions | boolean = { isLe: false, isNegative: false } +): BN { + if (!value) { + return new BN(0); + } + + const _options: ToBnOptions = { + isLe: false, + isNegative: false, + // Backwards-compatibility + ...(isBoolean(options) ? { isLe: options } : options) + }; + + const _value = hexStripPrefix(value as string); + + // FIXME: Use BN's 3rd argument `isLe` once this issue is fixed + // https://github.com/indutny/bn.js/issues/208 + const bn = new BN((_options.isLe ? reverse(_value) : _value) || '00', 16); + + // fromTwos takes as parameter the number of bits, which is the hex length + // multiplied by 4. + return _options.isNegative ? bn.fromTwos(_value.length * 4) : bn; +} diff --git a/packages/util/src/hex/toNumber.spec.ts b/packages/util/src/hex/toNumber.spec.ts new file mode 100644 index 0000000000..5e67dfbd14 --- /dev/null +++ b/packages/util/src/hex/toNumber.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToNumber } from '.'; + +describe('hexToNumber', () => { + it('converts an empty to NaN', () => { + expect( + hexToNumber() + ).toEqual(NaN); + }); + + it('converts to a number from hex', () => { + expect( + hexToNumber('0x1234') + ).toEqual(0x1234); + }); +}); diff --git a/packages/util/src/hex/toNumber.ts b/packages/util/src/hex/toNumber.ts new file mode 100644 index 0000000000..047de0e9f7 --- /dev/null +++ b/packages/util/src/hex/toNumber.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hexToBn from './toBn'; + +/** + * @name hexToNumber + * @summary Creates a Number value from a Buffer object. + * @description + * `null` inputs returns an NaN result, `hex` values return the actual value as a `Number`. + * @example + *
+ * + * ```javascript + * import { hexToNumber } from '@plugnet/util'; + * + * hexToNumber('0x1234'); // => 0x1234 + * ``` + */ +export default function hexToNumber (value?: string | null): number { + if (!value) { + return NaN; + } + + return hexToBn(value).toNumber(); +} diff --git a/packages/util/src/hex/toU8a.spec.ts b/packages/util/src/hex/toU8a.spec.ts new file mode 100644 index 0000000000..f43b88af78 --- /dev/null +++ b/packages/util/src/hex/toU8a.spec.ts @@ -0,0 +1,35 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { hexToU8a } from '.'; + +describe('hexToU8a', () => { + it('returns an empty Uint8Array when null provided', () => { + expect( + hexToU8a(null) + ).toHaveLength(0); + }); + + it('returns a Uint8Array with the correct values', () => { + expect( + hexToU8a('0x80000a') + ).toEqual( + new Uint8Array([128, 0, 10]) + ); + }); + + it('returns a Uint8Array with the correct values (bitLengfth)', () => { + expect( + hexToU8a('0x80000a', 32) + ).toEqual( + new Uint8Array([0, 128, 0, 10]) + ); + }); + + it('fails when non-hex value provided', () => { + expect( + () => hexToU8a('notahex') + ).toThrow(/hex value to convert/); + }); +}); diff --git a/packages/util/src/hex/toU8a.ts b/packages/util/src/hex/toU8a.ts new file mode 100644 index 0000000000..dcedb0ea10 --- /dev/null +++ b/packages/util/src/hex/toU8a.ts @@ -0,0 +1,46 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import assert from '../assert'; +import isHex from '../is/hex'; +import hexStripPrefix from './stripPrefix'; + +/** + * @name hexToU8a + * @summary Creates a Buffer object from a hex string. + * @description + * `null` inputs returns an empty `Uint8Array` result. Hex input values return the actual bytes value converted to a Uint8Array. Anything that is not a hex string (including the `0x` prefix) throws an error. + * @example + *
+ * + * ```javascript + * import { hexToU8a } from '@plugnet/util'; + * + * hexToU8a('0x80001f'); // Uint8Array([0x80, 0x00, 0x1f]) + * hexToU8a('0x80001f', 32); // Uint8Array([0x00, 0x80, 0x00, 0x1f]) + * ``` + */ +export default function hexToU8a (_value?: string | null, bitLength: number = -1): Uint8Array { + if (!_value) { + return new Uint8Array([]); + } + + assert(isHex(_value), `Expected hex value to convert, found '${_value}'`); + + const value = hexStripPrefix(_value); + const valLength = value.length / 2; + const bufLength = Math.ceil( + bitLength === -1 + ? valLength + : bitLength / 8 + ); + const result = new Uint8Array(bufLength); + const offset = Math.max(0, bufLength - valLength); + + for (let index = 0; index < bufLength; index++) { + result[index + offset] = parseInt(value.substr(index * 2, 2), 16); + } + + return result; +} diff --git a/packages/util/src/index.spec.ts b/packages/util/src/index.spec.ts new file mode 100644 index 0000000000..2ae6940ead --- /dev/null +++ b/packages/util/src/index.spec.ts @@ -0,0 +1,11 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { assert } from '.'; + +describe('index', () => { + it('exports ok', () => { + expect(assert).toBeDefined(); + }); +}); diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts new file mode 100644 index 0000000000..3bb8f1112c --- /dev/null +++ b/packages/util/src/index.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import './polyfill'; + +/** + * @summary Utility methods for this package are split into groups + */ + +export { default as assert } from './assert'; +export { default as assertSingletonPackage } from './assertSingletonPackage'; +export { default as logger } from './logger'; +export { default as promisify } from './promisify'; + +export * from './array'; +export * from './bn'; +export * from './buffer'; +export * from './compact'; +export * from './ext'; +export * from './format'; +export * from './hex'; +export * from './is'; +export * from './number'; +export * from './string'; +export * from './u8a'; diff --git a/packages/util/src/is/bn.spec.ts b/packages/util/src/is/bn.spec.ts new file mode 100644 index 0000000000..3bfc85ea24 --- /dev/null +++ b/packages/util/src/is/bn.spec.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import { isBn } from '.'; + +describe('isBN', () => { + it('returns true when a BN value', () => { + expect( + isBn(new BN(123)) + ).toEqual(true); + }); + + it('returns false on non-BN values', () => { + expect( + isBn(0) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/bn.ts b/packages/util/src/is/bn.ts new file mode 100644 index 0000000000..4bbb95828f --- /dev/null +++ b/packages/util/src/is/bn.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +/** + * @name isBn + * @summary Tests for a `BN` object instance. + * @description + * Checks to see if the input object is an instance of `BN` (bn.js). + * @example + *
+ * + * ```javascript + * import BN from 'bn.js'; + * import { isBn } from '@plugnet/util'; + * + * console.log('isBn', isBn(new BN(1))); // => true + * ``` + */ +export default function isBn (value: any): value is BN { + return BN.isBN(value); +} diff --git a/packages/util/src/is/boolean.spec.ts b/packages/util/src/is/boolean.spec.ts new file mode 100644 index 0000000000..914d8ff7d4 --- /dev/null +++ b/packages/util/src/is/boolean.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isBoolean } from '.'; + +describe('isBoolean', () => { + it('returns true on false', () => { + expect( + isBoolean(false) + ).toEqual(true); + }); + + it('returns true on true', () => { + expect( + isBoolean(true) + ).toEqual(true); + }); + + it('returns false on invalid booleans', () => { + expect( + isBoolean('notABool') + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/boolean.ts b/packages/util/src/is/boolean.ts new file mode 100644 index 0000000000..9e7da849e3 --- /dev/null +++ b/packages/util/src/is/boolean.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isBoolean + * @summary Tests for a boolean value. + * @description + * Checks to see if the input value is a JavaScript boolean. + * @example + *
+ * + * ```javascript + * import { isBoolean } from '@plugnet/util'; + * + * isBoolean(false); // => true + * ``` + */ +export default function isBoolean (value: any): value is boolean { + return typeof value === 'boolean'; +} diff --git a/packages/util/src/is/buffer.spec.ts b/packages/util/src/is/buffer.spec.ts new file mode 100644 index 0000000000..6704044811 --- /dev/null +++ b/packages/util/src/is/buffer.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isBuffer } from '.'; + +describe('isBuffer', () => { + it('returns true when a Buffer value', () => { + expect( + isBuffer(Buffer.from([])) + ).toEqual(true); + }); + + it('returns false on non-Buffer values', () => { + expect( + isBuffer(0) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/buffer.ts b/packages/util/src/is/buffer.ts new file mode 100644 index 0000000000..929340ad68 --- /dev/null +++ b/packages/util/src/is/buffer.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isInstanceOf from './instanceOf'; + +/** + * @name isBuffer + * @summary Tests for a `Buffer` object instance. + * @description + * Checks to see if the input object is an instance of `Buffer`. + * @example + *
+ * + * ```javascript + * import { isBuffer } from '@plugnet/util'; + * + * console.log('isBuffer', isBuffer(Buffer.from([]))); // => true + * ``` + */ +export default function isBuffer (value: any): value is Buffer { + return isInstanceOf(value, Buffer); +} diff --git a/packages/util/src/is/error.spec.ts b/packages/util/src/is/error.spec.ts new file mode 100644 index 0000000000..f34c337b92 --- /dev/null +++ b/packages/util/src/is/error.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isError } from '.'; + +describe('isError', () => { + it('returns true when an Error value', () => { + expect( + isError(new Error('testing')) + ).toEqual(true); + }); + + it('returns false on non-Error values', () => { + expect( + isError(0) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/error.ts b/packages/util/src/is/error.ts new file mode 100644 index 0000000000..29063ce484 --- /dev/null +++ b/packages/util/src/is/error.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isInstanceOf from './instanceOf'; + +/** + * @name isError + * @summary Tests for a `Error` object instance. + * @description + * Checks to see if the input object is an instance of `Error`. + * @example + *
+ * + * ```javascript + * import { isError } from '@plugnet/util'; + * + * console.log('isError', isError(new Error('message'))); // => true + * ``` + */ +export default function isError (value: any): value is Error { + return isInstanceOf(value, Error); +} diff --git a/packages/util/src/is/function.spec.ts b/packages/util/src/is/function.spec.ts new file mode 100644 index 0000000000..9b06bca20a --- /dev/null +++ b/packages/util/src/is/function.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isFunction } from '.'; + +describe('isFunction', () => { + it('returns true on valid functions', () => { + expect( + isFunction(isFunction) + ).toEqual(true); + }); + + it('returns false on invalid functions', () => { + expect( + isFunction('notAFunction') + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/function.ts b/packages/util/src/is/function.ts new file mode 100644 index 0000000000..62b55a2749 --- /dev/null +++ b/packages/util/src/is/function.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isFunction + * @summary Tests for a `function`. + * @description + * Checks to see if the input value is a JavaScript function. + * @example + *
+ * + * ```javascript + * import { isFunction } from '@plugnet/util'; + * + * isFunction(() => false); // => true + * ``` + */ +export default function isFunction (value: any): value is Function { + return typeof value === 'function'; +} diff --git a/packages/util/src/is/hex.spec.ts b/packages/util/src/is/hex.spec.ts new file mode 100644 index 0000000000..342016a36e --- /dev/null +++ b/packages/util/src/is/hex.spec.ts @@ -0,0 +1,63 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isHex } from '.'; + +describe('isHex', () => { + const test = '1234abcd'; + + it('returns true on 0x hex values', () => { + expect( + isHex('0x') + ).toEqual(true); + }); + + it('returns true on hex values', () => { + expect( + isHex(`0x${test}`) + ).toEqual(true); + }); + + it('returns true on hex values with String', () => { + expect( + isHex(new String(`0x${test}`)) + ).toEqual(true); + }); + + it('returns false on hex values (non % 2)', () => { + expect( + isHex(`0x${test}0`) + ).toEqual(false); + }); + + it('returns true on uppercase values', () => { + expect( + isHex(`0x${test.toUpperCase()}`) + ).toEqual(true); + }); + + it('return false on hex values unprefixed', () => { + expect( + isHex(test) + ).toEqual(false); + }); + + it('returns false on non-string values', () => { + expect( + isHex(false) + ).toEqual(false); + }); + + it('returns true when valid hex and bitLength matches', () => { + expect( + isHex('0x1234', 16) + ).toEqual(true); + }); + + it('returns true when valid hex and bitLength does not match', () => { + expect( + isHex('0x1234', 8) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/hex.ts b/packages/util/src/is/hex.ts new file mode 100644 index 0000000000..62daa9c41e --- /dev/null +++ b/packages/util/src/is/hex.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isString from './string'; + +const HEX_REGEX = /^0x[a-fA-F0-9]+$/; + +/** + * @name isHex + * @summary Tests for a hex string. + * @description + * Checks to see if the input value is a `0x` prefixed hex string. Optionally (`bitLength` !== -1) checks to see if the bitLength is correct. + * @example + *
+ * + * ```javascript + * import { isHex } from '@plugnet/util'; + * + * isHex('0x1234'); // => true + * isHex('0x1234', 8); // => false + * ``` + */ +export default function isHex (value: any, bitLength: number = -1, ignoreLength: boolean = false): value is string | String { + const isValidHex = value === '0x' || (isString(value) && HEX_REGEX.test(value.toString())); + + if (isValidHex && bitLength !== -1) { + return value.length === (2 + Math.ceil(bitLength / 4)); + } + + return isValidHex && (ignoreLength || (value.length % 2 === 0)); +} diff --git a/packages/util/src/is/index.ts b/packages/util/src/is/index.ts new file mode 100644 index 0000000000..d1cde591da --- /dev/null +++ b/packages/util/src/is/index.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Type checking utilities + */ + +export { default as isBn } from './bn'; +export { default as isBuffer } from './buffer'; +export { default as isBoolean } from './boolean'; +export { default as isError } from './error'; +export { default as isFunction } from './function'; +export { default as isHex } from './hex'; +export { default as isInstanceOf } from './instanceOf'; +export { default as isIp } from './ip'; +export { default as isJsonObject } from './jsonObject'; +export { default as isNull } from './null'; +export { default as isNumber } from './number'; +export { default as isObject } from './object'; +export { default as isObservable } from './observable'; +export { default as isString } from './string'; +export { default as isTestChain } from './testChain'; +export { default as isU8a } from './u8a'; +export { default as isUndefined } from './undefined'; diff --git a/packages/util/src/is/instanceOf.spec.ts b/packages/util/src/is/instanceOf.spec.ts new file mode 100644 index 0000000000..c83384044e --- /dev/null +++ b/packages/util/src/is/instanceOf.spec.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isInstanceOf } from '.'; + +describe('isInstanceOf', () => { + it('returns true on real instances', () => { + expect( + isInstanceOf(new Array(2), Array) + ).toEqual(true); + }); + + it('returns false on non-allocated instances', () => { + expect( + isInstanceOf([], Array) + ).toEqual(true); + }); + + it('returns false on non-instances', () => { + expect( + isInstanceOf('array', Array) + ).toEqual(false); + }); + + it('returns false when class not specified', () => { + expect( + isInstanceOf('array', Array) + ).toEqual(false); + }); + + it('returns false on wrong class type', () => { + expect( + isInstanceOf(new Array(2), String) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/instanceOf.ts b/packages/util/src/is/instanceOf.ts new file mode 100644 index 0000000000..ffb06b20e7 --- /dev/null +++ b/packages/util/src/is/instanceOf.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isInstanceOf + * @summary Tests for a instance of a class. + * @description + * Checks to see if the input value is an instance of the test class. + * @example + *
+ * + * ```javascript + * import { isInstanceOf } from '@plugnet/util'; + * + * console.log('isInstanceOf', isInstanceOf(new Array(0), Array)); // => true + * ``` + */ +export default function isInstanceOf (value: any, clazz: any): boolean { + return value instanceof clazz; +} diff --git a/packages/util/src/is/ip.spec.ts b/packages/util/src/is/ip.spec.ts new file mode 100644 index 0000000000..1a9210c1d6 --- /dev/null +++ b/packages/util/src/is/ip.spec.ts @@ -0,0 +1,49 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isIp } from '.'; + +describe('isIp', () => { + it('returns false when non-ip found', () => { + expect( + isIp('not.an.ip.1') + ).toEqual(false); + }); + + it('returns true on IPv4 addresses', () => { + expect( + isIp('192.168.0.1') + ).toEqual(true); + }); + + it('returns true on IPv4 addresses (IPV4 type)', () => { + expect( + isIp('192.168.0.1', 'v4') + ).toEqual(true); + }); + + it('returns false on IPv4 addresses (IPv6 type)', () => { + expect( + isIp('192.168.0.1', 'v6') + ).toEqual(false); + }); + + it('returns true on IPv6 addresses', () => { + expect( + isIp('1:2:3:4:5:6:7:8') + ).toEqual(true); + }); + + it('returns true on IPv6 addresses (IPv6 type)', () => { + expect( + isIp('1:2:3:4:5:6:7:8', 'v6') + ).toEqual(true); + }); + + it('returns false on IPv6 addresses (IPv4 type)', () => { + expect( + isIp('1:2:3:4:5:6:7:8', 'v4') + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/ip.ts b/packages/util/src/is/ip.ts new file mode 100644 index 0000000000..e00ca423db --- /dev/null +++ b/packages/util/src/is/ip.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import ipRegex from 'ip-regex'; + +type IpTypes = 'v4' | 'v6'; + +/** + * @name isIp + * @summary Tests if the value is a valid IP address + * @description + * Checks to see if the value is a valid IP address. Optionally check for either v4/v6 + * @example + *
+ * + * ```javascript + * import { isIp } from '@plugnet/util'; + * + * isIp('192.168.0.1')); // => true + * isIp('1:2:3:4:5:6:7:8'); // => true + * isIp('192.168.0.1', 'v6')); // => false + * isIp('1:2:3:4:5:6:7:8', 'v4'); // => false + * ``` + */ +export default function isIp (value: string, type?: IpTypes): boolean { + // FIXME @types/ip-regex defintions are outdated + if (type === 'v4') { + // @ts-ignore + return ipRegex.v4({ exact: true }).test(value); + } else if (type === 'v6') { + // @ts-ignore + return ipRegex.v6({ exact: true }).test(value); + } + + return ipRegex({ exact: true }).test(value); +} diff --git a/packages/util/src/is/jsonObject.spec.ts b/packages/util/src/is/jsonObject.spec.ts new file mode 100644 index 0000000000..002a9457d0 --- /dev/null +++ b/packages/util/src/is/jsonObject.spec.ts @@ -0,0 +1,50 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isJsonObject } from '.'; + +const jsonObject = `{ + "Test": "1234", + "NestedTest": { + "Test": "5678" + } + }`; + +describe('isJsonObject', () => { + it('returns true on empty objects', () => { + expect( + isJsonObject({}) + ).toEqual(true); + }); + + it('returns true on JSON objects', () => { + expect( + isJsonObject(jsonObject) + ).toEqual(true); + }); + + it('returns false on JSON parsable value typeof number', () => { + expect( + isJsonObject(1234) + ).toEqual(false); + }); + + it('returns false on JSON parsable value null', () => { + expect( + isJsonObject(null) + ).toEqual(false); + }); + + it('returns false on invalid objects', () => { + expect( + isJsonObject('notAnObject') + ).toEqual(false); + }); + + it('returns false on invalid JSON', () => { + expect( + isJsonObject(`{"abc", "def"}`) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/jsonObject.ts b/packages/util/src/is/jsonObject.ts new file mode 100644 index 0000000000..7eef964a91 --- /dev/null +++ b/packages/util/src/is/jsonObject.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +type ObjectIndexed = { + [index: string]: any +}; + +/** + * @name isJsonObject + * @summary Tests for a valid JSON `object`. + * @description + * Checks to see if the input value is a valid JSON object. + * It returns false if the input is JSON parsable, but not an Javascript object. + * @example + *
+ * + * ```javascript + * import { isJsonObject } from '@plugnet/util'; + * + * isJsonObject({}); // => true + * isJsonObject({ + * "Test": "1234", + * "NestedTest": { + * "Test": "5678" + * } + * }); // => true + * isJsonObject(1234); // JSON parsable, but not an object => false + * isJsonObject(null); // JSON parsable, but not an object => false + * isJsonObject('not an object'); // => false + * ``` + */ + +export default function isJsonObject (value: any): value is ObjectIndexed { + + value = typeof value !== 'string' + ? JSON.stringify(value) + : value; + + try { + value = JSON.parse(value); + + return (typeof value === 'object' && value !== null); + } catch (e) { + return false; + } +} diff --git a/packages/util/src/is/null.spec.ts b/packages/util/src/is/null.spec.ts new file mode 100644 index 0000000000..96bde2dba4 --- /dev/null +++ b/packages/util/src/is/null.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isNull } from '.'; + +describe('isNull', () => { + it('returns true when a null value', () => { + expect( + isNull(null) + ).toEqual(true); + }); + + it('returns false on non-null values', () => { + expect( + isNull() + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/null.ts b/packages/util/src/is/null.ts new file mode 100644 index 0000000000..5a34fefef8 --- /dev/null +++ b/packages/util/src/is/null.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isNull + * @summary Tests for a `null` values. + * @description + * Checks to see if the input value is `null`. + * @example + *
+ * + * ```javascript + * import { isNull } from '@plugnet/util'; + * + * console.log('isNull', isNull(null)); // => true + * ``` + */ +export default function isNull (value?: any): value is null { + return value === null; +} diff --git a/packages/util/src/is/number.spec.ts b/packages/util/src/is/number.spec.ts new file mode 100644 index 0000000000..2617550db5 --- /dev/null +++ b/packages/util/src/is/number.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isNumber } from '.'; + +describe('isNumber', () => { + it('returns true on valid numbers', () => { + expect( + isNumber(2) + ).toEqual(true); + }); + + it('returns false on invalid numbers', () => { + expect( + isNumber('2') + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/number.ts b/packages/util/src/is/number.ts new file mode 100644 index 0000000000..a5c7a0bd5a --- /dev/null +++ b/packages/util/src/is/number.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isNumber + * @summary Tests for a JavaScript number. + * @description + * Checks to see if the input value is a valid number. + * @example + *
+ * + * ```javascript + * import { isNumber } from '@plugnet/util'; + * + * console.log('isNumber', isNumber(1234)); // => true + * ``` + */ +export default function isNumber (value: any): value is number { + return typeof value === 'number'; +} diff --git a/packages/util/src/is/object.spec.ts b/packages/util/src/is/object.spec.ts new file mode 100644 index 0000000000..d0ea408f54 --- /dev/null +++ b/packages/util/src/is/object.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isObject } from '.'; + +describe('isObject', () => { + it('returns true on valid objects', () => { + expect( + isObject({}) + ).toEqual(true); + }); + + it('returns false on invalid objects', () => { + expect( + isObject('notAnObject') + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/object.ts b/packages/util/src/is/object.ts new file mode 100644 index 0000000000..d9ed8d3e57 --- /dev/null +++ b/packages/util/src/is/object.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +type ObjectIndexed = { + [index: string]: any +}; + +/** + * @name isObject + * @summary Tests for an `object`. + * @description + * Checks to see if the input value is a JavaScript object. + * @example + *
+ * + * ```javascript + * import { isObject } from '@plugnet/util'; + * + * isObject({}); // => true + * isObject('something'); // => false + * ``` + */ +export default function isObject (value: any): value is ObjectIndexed { + return typeof value === 'object'; +} diff --git a/packages/util/src/is/observable.spec.ts b/packages/util/src/is/observable.spec.ts new file mode 100644 index 0000000000..e697272dc5 --- /dev/null +++ b/packages/util/src/is/observable.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isObservable } from '.'; + +describe('isObservable', () => { + it('returns true on valid observables', () => { + expect( + isObservable({ next: () => true }) + ).toEqual(true); + }); + + it('returns false on invalid objects', () => { + expect( + isObservable('notAnObservable') + ).toEqual(false); + }); + + it('returns false on invalid next functions', () => { + expect( + isObservable({ next: true }) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/observable.ts b/packages/util/src/is/observable.ts new file mode 100644 index 0000000000..fe8d29b738 --- /dev/null +++ b/packages/util/src/is/observable.ts @@ -0,0 +1,28 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isFunction from './function'; +import isObject from './object'; + +type Observable = { + next: (...paarams: any[]) => any +}; + +/** + * @name isBObservable + * @summary Tests for a `Observable` object instance. + * @description + * Checks to see if the input object is an instance of `BN` (bn.js). + * @example + *
+ * + * ```javascript + * import { isObservable } from '@plugnet/util'; + * + * console.log('isObservable', isObservable(...)); + * ``` + */ +export default function isObservable (value: any): value is Observable { + return isObject(value) && isFunction((value as Observable).next); +} diff --git a/packages/util/src/is/string.spec.ts b/packages/util/src/is/string.spec.ts new file mode 100644 index 0000000000..0f2e17fee3 --- /dev/null +++ b/packages/util/src/is/string.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isString } from '.'; + +describe('isString', () => { + it('returns true on valid strings', () => { + expect( + isString('123') + ).toEqual(true); + }); + + it('returns true on empty strings', () => { + expect( + isString('') + ).toEqual(true); + }); + + it('returns true on String object', () => { + expect( + isString(new String('foo')) + ).toEqual(true); + }); + + it('returns false on invalid numbers', () => { + expect( + isString(2) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/string.ts b/packages/util/src/is/string.ts new file mode 100644 index 0000000000..25bba62f56 --- /dev/null +++ b/packages/util/src/is/string.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isString + * @summary Tests for a string. + * @description + * Checks to see if the input value is a JavaScript string. + * @example + *
+ * + * ```javascript + * import { isString } from '@plugnet/util'; + * + * console.log('isString', isString('test')); // => true + * ``` + */ +export default function isString (value: any): value is string | String { + return typeof value === 'string' || value instanceof String; +} diff --git a/packages/util/src/is/testChain.spec.ts b/packages/util/src/is/testChain.spec.ts new file mode 100644 index 0000000000..7fb8e1f3c1 --- /dev/null +++ b/packages/util/src/is/testChain.spec.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isTestChain from './testChain'; + +describe('isTestChain', () => { + it('enables test environment when chain specification matches text of dev or loc(al)', () => { + const validTestModeChainSpecsWithDev = ['Development']; + const validTestModeChainSpecsWithLoc = ['Local Testnet']; + + validTestModeChainSpecsWithDev.concat(validTestModeChainSpecsWithLoc).forEach((s) => + expect(isTestChain(s)).toEqual(true) + ); + }); + + it('disables keyring test mode when chain specification is not a test mode or undefined or number type', () => { + const invalidTestModeChainSpecs = ['dev', 'local', 'development', 'PoC-1 Testnet', 'Staging Testnet', 'future PoC-2 Testnet', 'a pocadot?', undefined]; + + invalidTestModeChainSpecs.forEach((s) => + expect(isTestChain(s)).toEqual(false) + ); + }); +}); diff --git a/packages/util/src/is/testChain.ts b/packages/util/src/is/testChain.ts new file mode 100644 index 0000000000..4238327a6e --- /dev/null +++ b/packages/util/src/is/testChain.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const re = new RegExp('^(Development|Local Testnet)$'); + +export default function isTestChain (chain?: string | null): boolean { + if (!chain) { + return false; + } + + return !!re.test(chain.toString()); +} diff --git a/packages/util/src/is/u8a.spec.ts b/packages/util/src/is/u8a.spec.ts new file mode 100644 index 0000000000..f1196b61c9 --- /dev/null +++ b/packages/util/src/is/u8a.spec.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isU8a } from '.'; + +describe('isUint8Array', () => { + it('returns false on undefined values', () => { + expect( + isU8a() + ).toEqual(false); + }); + + it('returns false on Array values', () => { + expect( + isU8a([]) + ).toEqual(false); + }); + + it('returns true on Uint8Array values', () => { + expect( + isU8a(new Uint8Array([])) + ).toEqual(true); + }); +}); diff --git a/packages/util/src/is/u8a.ts b/packages/util/src/is/u8a.ts new file mode 100644 index 0000000000..3b1bbfcf4f --- /dev/null +++ b/packages/util/src/is/u8a.ts @@ -0,0 +1,23 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isInstanceOf from './instanceOf'; + +/** + * @name isU8a + * @summary Tests for a `Uint8Array` object instance. + * @description + * Checks to see if the input object is an instance of `Uint8Array`. + * @example + *
+ * + * ```javascript + * import { isUint8Array } from '@plugnet/util'; + * + * console.log('isU8a', isU8a([])); // => false + * ``` + */ +export default function isU8a (value?: any): value is Uint8Array { + return isInstanceOf(value, Uint8Array); +} diff --git a/packages/util/src/is/undefined.spec.ts b/packages/util/src/is/undefined.spec.ts new file mode 100644 index 0000000000..84a1226c7e --- /dev/null +++ b/packages/util/src/is/undefined.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { isUndefined } from '.'; + +describe('isUndefined', () => { + it('returns true on undefined values', () => { + expect( + isUndefined() + ).toEqual(true); + }); + + it('returns false on defined values', () => { + expect( + isUndefined(null) + ).toEqual(false); + }); +}); diff --git a/packages/util/src/is/undefined.ts b/packages/util/src/is/undefined.ts new file mode 100644 index 0000000000..34bb725167 --- /dev/null +++ b/packages/util/src/is/undefined.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name isUndefined + * @summary Tests for a `undefined` values. + * @description + * Checks to see if the input value is `undefined`. + * @example + *
+ * + * ```javascript + * import { isUndefined } from '@plugnet/util'; + * + * console.log('isUndefined', isUndefined(void(0))); // => true + * ``` + */ +export default function isUndefined (value?: any): value is undefined { + return typeof value === 'undefined'; +} diff --git a/packages/util/src/logger.spec.ts b/packages/util/src/logger.spec.ts new file mode 100644 index 0000000000..dafb2eb864 --- /dev/null +++ b/packages/util/src/logger.spec.ts @@ -0,0 +1,158 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { logger } from '.'; +import { format } from './logger'; + +describe('logger', () => { + let dateMatch: any; + let prefixMatch: any; + let l: any; + let ln: any; + let spy: any; + let oldEnv: any; + + beforeEach(() => { + oldEnv = process.env; + process.env.NODE_ENV = 'development'; + + ln = logger('notDebug'); + + process.env.DEBUG = 'test'; + + l = logger('test'); + + spy = { + error: jest.fn(), + log: jest.fn(), + warn: jest.fn() + }; + (global as any).console = spy; + + dateMatch = expect.stringMatching(/20[0-9]{2}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/); + prefixMatch = expect.stringMatching(/TEST:/); + }); + + afterEach(() => { + process.env = oldEnv; + jest.restoreAllMocks(); + }); + + describe('format', () => { + it('returns null as-is', () => { + expect(format(null)).toEqual(null); + }); + + it('returns undefined as-is', () => { + expect(format(undefined)).toEqual(undefined); + }); + }); + + it('logs to console.log with .log', () => { + l.log('console.log test'); + + expect(spy.log).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('console.log test') + ); + }); + + it('logs with mulptiple values', () => { + l.log('test', 'a', 2); + + expect(spy.log).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('test'), + expect.stringMatching('a'), + 2 + ); + }); + + it('logs when called with function', () => { + l.log(() => 'a function test'); + + expect(spy.log).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('a function test') + ); + }); + + it('logs to console.error with .error', () => { + l.error('console.error test'); + + expect(spy.error).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('console.error test') + ); + }); + + it('logs to console.warn with .warn', () => { + l.warn('console.warn test'); + + expect(spy.warn).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('console.warn test') + ); + }); + + it('does debug log when DEBUG specified', () => { + l.debug('test'); + + expect(spy.log).toHaveBeenCalledWith( + dateMatch, + prefixMatch, + expect.stringMatching('test') + ); + }); + + it('does debug log when DEBUG partial specified', () => { + l = logger('testing'); + l.debug('test'); + + expect(spy.log).toHaveBeenCalledWith( + dateMatch, + expect.stringMatching(/TESTING:/), + expect.stringMatching('test') + ); + }); + + it('does not debug log when non-matching DEBUG specified', () => { + process.env.DEBUG = 'blah'; + + l = logger('test'); + l.debug('test'); + + expect(spy.log).not.toHaveBeenCalled(); + }); + + it('does debug log when NODE_ENV=test specified', () => { + process.env.DEBUG = 'blah'; + process.env.NODE_ENV = 'test'; + + l = logger('test'); + l.debug('test'); + + expect(spy.log).toHaveBeenCalled(); + }); + + it('does not debug log when no process.env', () => { + process.env = void 0 as any; + + l = logger('test'); + l.debug('test'); + + expect(spy.log).not.toHaveBeenCalled(); + }); + + it('does not debug log when no DEBUG specified', () => { + ln.debug('test'); + + expect(spy.log).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/util/src/logger.ts b/packages/util/src/logger.ts new file mode 100644 index 0000000000..2613dace12 --- /dev/null +++ b/packages/util/src/logger.ts @@ -0,0 +1,126 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { Logger, Logger$Data } from './types'; + +import chalk from 'chalk'; +import moment from 'moment'; + +import isBn from './is/bn'; +import isBuffer from './is/buffer'; +import isFunction from './is/function'; +import isObject from './is/object'; +import isU8a from './is/u8a'; +import u8aToHex from './u8a/toHex'; + +type ConsoleType = 'error' | 'log' | 'warn'; +type LogType = ConsoleType | 'debug'; + +const logTo = { + debug: 'log', + error: 'error', + log: 'log', + warn: 'warn' +}; + +const chalked = { + debug: chalk.gray, + error: chalk.red, + log: chalk.reset, + warn: chalk.yellow +}; + +function formatObject (value: { [index: string]: any }): any { + return Object.keys(value).reduce((result, key) => { + result[key] = format(value[key]); + + return result; + }, {} as { [index: string]: any }); +} + +export function format (value: any): any { + if (Array.isArray(value)) { + return value.map(format); + } + + if (isBn(value)) { + return value.toString(); + } + + if (isBuffer(value)) { + return `0x${value.toString('hex')}`; + } + + if (isU8a(value)) { + return u8aToHex(value); + } + + if (value && isObject(value) && value.constructor === Object) { + return formatObject(value); + } + + return value; +} + +function apply (log: LogType, type: string, values: Logger$Data): void { + if (values.length === 1 && isFunction(values[0])) { + const fnResult = (values[0] as Function)(); + + return apply(log, type, Array.isArray(fnResult) ? fnResult : [fnResult]); + } + + const chalk = (value: string): string => + chalked[log](value); + + // @ts-ignore Not sure how to coax TS here... + console[logTo[log]].apply( + console, [ + chalk(moment().format('YYYY-MM-DD HH:mm:ss')), chalk(type) + ].concat( + values.map(format) + ) + ); +} + +function noop (...values: Logger$Data): void { + // noop +} + +/** + * @name Logger + * @summary Creates a consistent log interface for messages + * @description + * Returns a `Logger` that has `.log`, `.error`, `.warn` and `.debug` (controlled with environment `DEBUG=typeA,typeB`) methods. Logging is done with a consistent prefix (type of logger, date) followed by the actual message using the underlying console. + * @example + *
+ * + * ```javascript + * const l from '@plugnet/util/logger')('test'); + * + * l.log('blah'); // TEST: blah + * ``` + */ +export default function logger (_type: string): Logger { + const type = `${_type.toUpperCase()}:`.padStart(16); + let isDebug; + + try { + const isTest = process.env.NODE_ENV === 'test'; + const debugList = (process.env.DEBUG || '').split(','); + + isDebug = isTest || !!debugList.find((entry) => _type.indexOf(entry) === 0); + } catch (error) { + isDebug = false; + } + + return { + debug: isDebug + ? (...values: Logger$Data): void => apply('debug', type, values) + : noop, + error: (...values: Logger$Data): void => apply('error', type, values), + log: (...values: Logger$Data): void => apply('log', type, values), + noop, + warn: (...values: Logger$Data): void => apply('warn', type, values) + }; +} diff --git a/packages/util/src/number/index.ts b/packages/util/src/number/index.ts new file mode 100644 index 0000000000..8a6fb9b252 --- /dev/null +++ b/packages/util/src/number/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods to convert to and from `number` values + */ + +export { default as numberToHex } from './toHex'; +export { default as numberToU8a } from './toU8a'; diff --git a/packages/util/src/number/toHex.spec.ts b/packages/util/src/number/toHex.spec.ts new file mode 100644 index 0000000000..016db91260 --- /dev/null +++ b/packages/util/src/number/toHex.spec.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { numberToHex } from '.'; + +describe('numberToHex', () => { + it('converts undefined to 0x', () => { + expect( + numberToHex() + ).toEqual('0x'); + }); + + it('converts null to 0x', () => { + expect( + numberToHex(null) + ).toEqual('0x'); + }); + + it('converts Nan to 0x', () => { + expect( + numberToHex(NaN) + ).toEqual('0x'); + }); + + it('converts 0 to 0x00', () => { + expect( + numberToHex(0) + ).toEqual('0x00'); + }); + + it('converts number to hex', () => { + expect( + numberToHex(0x1245) + ).toEqual('0x1245'); + }); + + it('converts number to hex (specfified bitLength)', () => { + expect( + numberToHex(0x1245, 32) + ).toEqual('0x00001245'); + }); +}); diff --git a/packages/util/src/number/toHex.ts b/packages/util/src/number/toHex.ts new file mode 100644 index 0000000000..7aa97b22ab --- /dev/null +++ b/packages/util/src/number/toHex.ts @@ -0,0 +1,34 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hexFixLength from '../hex/fixLength'; +import isNull from '../is/null'; +import isUndefined from '../is/undefined'; + +/** + * @name numberToHex + * @summary Creates a hex value from a number. + * @description + * `null`/`undefined`/`NaN` inputs returns an empty `0x` result. `number` input values return the actual bytes value converted to a `hex`. With `bitLength` set, it converts the number to the equivalent size. + * @example + *
+ * + * ```javascript + * import { numberToHex } from '@plugnet/util'; + * + * numberToHex(0x1234); // => '0x1234' + * numberToHex(0x1234, 32); // => 0x00001234 + * ``` + */ +export default function numberToHex (value?: number | null, bitLength: number = -1): string { + if (isUndefined(value) || isNull(value) || isNaN(value)) { + return '0x'; + } + + return hexFixLength( + (value || 0).toString(16), + bitLength, + true + ); +} diff --git a/packages/util/src/number/toU8a.spec.ts b/packages/util/src/number/toU8a.spec.ts new file mode 100644 index 0000000000..310d941ac5 --- /dev/null +++ b/packages/util/src/number/toU8a.spec.ts @@ -0,0 +1,43 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { numberToU8a } from '.'; + +describe('numberToU8a', () => { + it('converts 0 to empty', () => { + expect( + numberToU8a(0) + ).toEqual(new Uint8Array([])); + }); + + it('converts undefined to empty', () => { + expect( + numberToU8a() + ).toEqual(new Uint8Array([])); + }); + + it('converts null to empty', () => { + expect( + numberToU8a(null) + ).toEqual(new Uint8Array([])); + }); + + it('converts NaN to empty', () => { + expect( + numberToU8a(NaN) + ).toEqual(new Uint8Array([])); + }); + + it('converts values to the u8a', () => { + expect( + numberToU8a(0x3456) + ).toEqual(new Uint8Array([0x34, 0x56])); + }); + + it('converts values to the u8a (bitLength)', () => { + expect( + numberToU8a(0x3456, 32) + ).toEqual(new Uint8Array([0x00, 0x00, 0x34, 0x56])); + }); +}); diff --git a/packages/util/src/number/toU8a.ts b/packages/util/src/number/toU8a.ts new file mode 100644 index 0000000000..3755fb1665 --- /dev/null +++ b/packages/util/src/number/toU8a.ts @@ -0,0 +1,30 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import hexToU8a from '../hex/toU8a'; +import numberToHex from './toHex'; + +/** + * @name numberToU8a + * @summary Creates a Uint8Array object from a number. + * @description + * `null`/`undefined`/`NaN` inputs returns an empty `Uint8Array` result. `number` input values return the actual bytes value converted to a `Uint8Array`. With `bitLength`, it converts the value to the equivalent size. + * @example + *
+ * + * ```javascript + * import { numberToU8a } from '@plugnet/util'; + * + * numberToU8a(0x1234); // => [0x12, 0x34] + * ``` + */ +export default function numberToU8a (value?: number | null, bitLength: number = -1): Uint8Array { + if (!value || isNaN(value)) { + return new Uint8Array([]); + } + + return hexToU8a( + numberToHex(value, bitLength) + ); +} diff --git a/packages/util/src/polyfill/fill.spec.ts b/packages/util/src/polyfill/fill.spec.ts new file mode 100644 index 0000000000..21cdce74db --- /dev/null +++ b/packages/util/src/polyfill/fill.spec.ts @@ -0,0 +1,22 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('Array.fill', () => { + let arrayFill: any; + + beforeEach(() => { + arrayFill = Array.prototype.fill; + Array.prototype.fill = null as any; + + require('./fill'); + }); + + afterEach(() => { + Array.prototype.fill = arrayFill; + }); + + it('uses the polyfills', () => { + expect([1, 2, 3, 4, 5, 6, 7, 8].fill(5, 3)).toEqual([1, 2, 3, 5, 5, 5, 5, 5]); + }); +}); diff --git a/packages/util/src/polyfill/fill.ts b/packages/util/src/polyfill/fill.ts new file mode 100644 index 0000000000..b79316b16f --- /dev/null +++ b/packages/util/src/polyfill/fill.ts @@ -0,0 +1,49 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +if (!Array.prototype.fill) { + Array.prototype.fill = function fill (value: any, start: number = 0, end?: number) { + // Steps 1-2. + if (!this) { + throw new TypeError('this is null or not defined'); + } + + const A = Object(this); + + // Steps 3-5. + const len = A.length >>> 0; + + // Steps 6-7. + const relativeStart = start >> 0; + + // Step 8. + let k = relativeStart < 0 + ? Math.max(len + relativeStart, 0) + : Math.min(relativeStart, len); + + // Steps 9-10. + const relativeEnd = end === undefined + ? len + : end >> 0; + + // Step 11. + const final = relativeEnd < 0 + ? Math.max(len + relativeEnd, 0) + : Math.min(relativeEnd, len); + + // Step 12. + while (k < final) { + A[k] = value; + k++; + } + + // Step 13. + return A; + }; +} + +if (!Uint8Array.prototype.fill) { + // @ts-ignore + Uint8Array.prototype.fill = Array.prototype.fill; +} diff --git a/packages/util/src/polyfill/index.ts b/packages/util/src/polyfill/index.ts new file mode 100644 index 0000000000..8c7cff392a --- /dev/null +++ b/packages/util/src/polyfill/index.ts @@ -0,0 +1,10 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import './fill'; +import './padEnd'; +import './padStart'; +import './setPrototypeOf'; +import './textDecoder'; +import './textEncoder'; diff --git a/packages/util/src/polyfill/padEnd.spec.ts b/packages/util/src/polyfill/padEnd.spec.ts new file mode 100644 index 0000000000..8ae6c62958 --- /dev/null +++ b/packages/util/src/polyfill/padEnd.spec.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('String padEnd', () => { + let stringEnd: (length: number, fill?: string) => string; + + beforeEach(() => { + stringEnd = String.prototype.padEnd; + + String.prototype.padEnd = null as any; + + require('./padEnd'); + }); + + afterEach(() => { + String.prototype.padEnd = stringEnd; + }); + + it('does padding', () => { + expect('test'.padEnd(8, 'A')).toEqual('testAAAA'); + expect('test'.padEnd(8)).toEqual('test '); + }); +}); diff --git a/packages/util/src/polyfill/padEnd.ts b/packages/util/src/polyfill/padEnd.ts new file mode 100644 index 0000000000..b1d4a888c3 --- /dev/null +++ b/packages/util/src/polyfill/padEnd.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +if (!String.prototype.padEnd) { + String.prototype.padEnd = function padEnd (length: number, char: string = ' ') { + let result: string = String(this); + + while (result.length < length) { + result = result + char; + } + + return result; + }; +} diff --git a/packages/util/src/polyfill/padStart.spec.ts b/packages/util/src/polyfill/padStart.spec.ts new file mode 100644 index 0000000000..175d82a4bc --- /dev/null +++ b/packages/util/src/polyfill/padStart.spec.ts @@ -0,0 +1,24 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('String padStart', () => { + let stringStart: (length: number, fill?: string) => string; + + beforeEach(() => { + stringStart = String.prototype.padStart; + + String.prototype.padStart = null as any; + + require('./padStart'); + }); + + afterEach(() => { + String.prototype.padStart = stringStart; + }); + + it('does padding', () => { + expect('test'.padStart(8, 'A')).toEqual('AAAAtest'); + expect('test'.padStart(8)).toEqual(' test'); + }); +}); diff --git a/packages/util/src/polyfill/padStart.ts b/packages/util/src/polyfill/padStart.ts new file mode 100644 index 0000000000..b660dcac97 --- /dev/null +++ b/packages/util/src/polyfill/padStart.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +if (!String.prototype.padStart) { + String.prototype.padStart = function padStart (length: number, char: string = ' ') { + let result: string = String(this); + + while (result.length < length) { + result = char + result; + } + + return result; + }; +} diff --git a/packages/util/src/polyfill/setPrototypeOf.ts b/packages/util/src/polyfill/setPrototypeOf.ts new file mode 100644 index 0000000000..e8fdf65dd3 --- /dev/null +++ b/packages/util/src/polyfill/setPrototypeOf.ts @@ -0,0 +1,12 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// React Native does not have Object.setPrototypeOf +if (!Object.setPrototypeOf) { + Object.setPrototypeOf = function (obj: any, proto: any) { + obj.__proto__ = proto; + + return obj; + }; +} diff --git a/packages/util/src/polyfill/textDecoder.spec.ts b/packages/util/src/polyfill/textDecoder.spec.ts new file mode 100644 index 0000000000..b2c62a814e --- /dev/null +++ b/packages/util/src/polyfill/textDecoder.spec.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('TextDecoder', () => { + let origTD: TextDecoder; + + beforeEach(() => { + origTD = (global as any).TextDecoder; + }); + + afterEach(() => { + (global as any).TextDecoder = origTD; + }); + + it('polyfills with no exceptions (without TextDecoder)', () => { + (global as any).TextDecoder = null; + + expect(require('./textDecoder')).toBeDefined(); + expect((global as any).TextDecoder).toBeDefined(); + }); + + it('polyfills with no exceptions (with TextDecoder)', () => { + (global as any).TextDecoder = require('util').TextDecoder; + + expect(require('./textDecoder')).toBeDefined(); + expect((global as any).TextDecoder).toBeDefined(); + }); +}); diff --git a/packages/util/src/polyfill/textDecoder.ts b/packages/util/src/polyfill/textDecoder.ts new file mode 100644 index 0000000000..6a0f8626d2 --- /dev/null +++ b/packages/util/src/polyfill/textDecoder.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// tslint:disable-next-line +if (typeof TextDecoder === 'undefined') { + try { + // @ts-ignore + global.TextDecoder = require('util').TextDecoder; + } catch (error) { + // noop + } +} diff --git a/packages/util/src/polyfill/textEncoder.spec.ts b/packages/util/src/polyfill/textEncoder.spec.ts new file mode 100644 index 0000000000..ef09a7ad9f --- /dev/null +++ b/packages/util/src/polyfill/textEncoder.spec.ts @@ -0,0 +1,29 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +describe('TextEncoder', () => { + let origTE: TextEncoder; + + beforeEach(() => { + origTE = (global as any).TextEncoder; + }); + + afterEach(() => { + (global as any).TextEncoder = origTE; + }); + + it('polyfills with no exceptions (without TextEncoder)', () => { + (global as any).TextEncoder = null; + + expect(require('./textEncoder')).toBeDefined(); + expect((global as any).TextEncoder).toBeDefined(); + }); + + it('polyfills with no exceptions (with TextEncoder)', () => { + (global as any).TextEncoder = require('util').TextEncoder; + + expect(require('./textEncoder')).toBeDefined(); + expect((global as any).TextEncoder).toBeDefined(); + }); +}); diff --git a/packages/util/src/polyfill/textEncoder.ts b/packages/util/src/polyfill/textEncoder.ts new file mode 100644 index 0000000000..8fe6a76e10 --- /dev/null +++ b/packages/util/src/polyfill/textEncoder.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +// tslint:disable-next-line +if (typeof TextEncoder === 'undefined') { + try { + // @ts-ignore For the Node.js case + global.TextEncoder = require('util').TextEncoder; + } catch (error) { + // noop + } +} diff --git a/packages/util/src/promisify.spec.ts b/packages/util/src/promisify.spec.ts new file mode 100644 index 0000000000..d7bdbdb5e1 --- /dev/null +++ b/packages/util/src/promisify.spec.ts @@ -0,0 +1,49 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { promisify } from '.'; + +describe('promisify', () => { + it('handles functions with no parameters (resolve)', () => { + const fn = (cb: Function) => cb(null, [true, 'test', 1]); + + return promisify(null, fn).then((result) => { + expect(result).toEqual([true, 'test', 1]); + }); + }); + + it('handles functions with no parameters (reject)', () => { + const fn = (cb: Function) => cb(new Error('test reject')); + + return promisify(null, fn).catch((error) => { + expect(error.message).toEqual('test reject'); + }); + }); + + it('handles functions with parameters (resolve)', () => { + const fn = (a: any, b: any, c: any, cb: Function) => cb(null, [a, b, c]); + + return promisify(null, fn, 2, false, null).then((result) => { + expect(result).toEqual([2, false, null]); + }); + }); + + it('handles functions with parameters (reject)', () => { + const fn = (a: any, b: any, c: any, cb: Function) => cb(new Error(`test reject: ${a},${b},${c}`)); + + return promisify(null, fn, 3, 'string', true).catch((error) => { + expect(error.message).toEqual('test reject: 3,string,true'); + }); + }); + + it('applies the correct this argument', () => { + const self = { something: 'something' }; + + return promisify(self, function (cb: Function) { + // @ts-ignore + expect(this).toEqual(self); + cb(); + }); + }); +}); diff --git a/packages/util/src/promisify.ts b/packages/util/src/promisify.ts new file mode 100644 index 0000000000..ace314eaee --- /dev/null +++ b/packages/util/src/promisify.ts @@ -0,0 +1,32 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name promisify + * @summary Wraps an async callback into a `Promise` + * @description + * Wraps the supplied async function `fn` that has a standard JS callback `(error: Error, result: any)` into a `Promise`, passing the supplied parameters. When `error` is set, the Promise is rejected, else the Promise resolves with the `result` value. + * @example + *
+ * + * ```javascript + * const { promisify } from '@plugnet/util'; + * + * await promisify(null, ((a, cb) => cb(null, a), true); // resolves with `true` + * await promisify(null, (cb) => cb(new Error('error!'))); // rejects with `error!` + * ``` + */ +export default function promisify (self: any, fn: Function, ...params: Array): Promise { + return new Promise((resolve, reject) => { + fn.apply(self, params.concat([ + (error: Error | null, result: any): void => { + if (error) { + reject(error); + } else { + resolve(result); + } + } + ])); + }); +} diff --git a/packages/util/src/string/camelCase.spec.ts b/packages/util/src/string/camelCase.spec.ts new file mode 100644 index 0000000000..bdb6e28973 --- /dev/null +++ b/packages/util/src/string/camelCase.spec.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringCamelCase } from '.'; + +describe('stringCamelCase', () => { + it('works correctly', () => { + expect( + stringCamelCase('Snake_case') + ).toBe('snakeCase'); + }); +}); diff --git a/packages/util/src/string/camelCase.ts b/packages/util/src/string/camelCase.ts new file mode 100644 index 0000000000..771553f082 --- /dev/null +++ b/packages/util/src/string/camelCase.ts @@ -0,0 +1,7 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import stringCamelCase from 'camelcase'; + +export default stringCamelCase; diff --git a/packages/util/src/string/index.ts b/packages/util/src/string/index.ts new file mode 100644 index 0000000000..a7857759d6 --- /dev/null +++ b/packages/util/src/string/index.ts @@ -0,0 +1,13 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods to convert to work with `string` values + */ + +export { default as stringCamelCase } from './camelCase'; +export { default as stringLowerFirst } from './lowerFirst'; +export { default as stringShorten } from './shorten'; +export { default as stringToU8a } from './toU8a'; +export { default as stringUpperFirst } from './upperFirst'; diff --git a/packages/util/src/string/lowerFirst.spec.ts b/packages/util/src/string/lowerFirst.spec.ts new file mode 100644 index 0000000000..a777db7078 --- /dev/null +++ b/packages/util/src/string/lowerFirst.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringLowerFirst } from '.'; + +describe('stringLowerFirst', () => { + it("lowers the first letter if it's a capital letter", () => { + expect( + stringLowerFirst('ABC') + ).toBe('aBC'); + }); + + it("lowers the first letter if it's a lowercase letter", () => { + expect( + stringLowerFirst('abc') + ).toBe('abc'); + }); + + it('returns undefined as empty', () => { + expect( + stringLowerFirst() + ).toBe(''); + }); + + it('returns null as empty', () => { + expect( + stringLowerFirst(null) + ).toBe(''); + }); +}); diff --git a/packages/util/src/string/lowerFirst.ts b/packages/util/src/string/lowerFirst.ts new file mode 100644 index 0000000000..7f2d90aa5f --- /dev/null +++ b/packages/util/src/string/lowerFirst.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name stringLowerFirst + * @summary Lowercase the first letter of a string + * @description + * Lowercase the first letter of a string + * @example + *
+ * + * ```javascript + * import { stringLowerFirst } from '@plugnet/util'; + * + * stringLowerFirst('ABC'); // => 'aBC' + * ``` + */ +export default function stringLowerFirst (value?: string | null): string { + if (!value) { + return ''; + } + + return value.charAt(0).toLowerCase() + value.slice(1); +} diff --git a/packages/util/src/string/shorten.spec.ts b/packages/util/src/string/shorten.spec.ts new file mode 100644 index 0000000000..7783dd5afa --- /dev/null +++ b/packages/util/src/string/shorten.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringShorten } from '.'; + +describe('stringShorten', () => { + it('returns the value as-is when <= maxLength', () => { + expect( + stringShorten('0123456789', 4) + ).toEqual('0123456789'); + }); + + it('returns the shortened value when > maxLength', () => { + expect( + stringShorten('0123456789', 3) + ).toEqual('012..789'); + }); + + it('returns undefined as undefined', () => { + expect( + stringShorten() + ).toEqual('undefined'); + }); + + it('returns non-string values as strings', () => { + expect( + stringShorten(12345678, 2) + ).toEqual('12..78'); + }); +}); diff --git a/packages/util/src/string/shorten.ts b/packages/util/src/string/shorten.ts new file mode 100644 index 0000000000..e1e7ffef67 --- /dev/null +++ b/packages/util/src/string/shorten.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name stringShorten + * @summary Returns a string with maximum length + * @description + * Checks the string against the `prefixLength`, if longer than dopuble this, shortens it by placing `..` in the middle of it + * @example + *
+ * + * ```javascript + * import { stringShorten } from '@plugnet/util'; + * + * stringShorten('1234567890', 2); // => 12..90 + * ``` + */ +export default function stringShorten (_value?: any, prefixLength: number = 6): string { + const value = `${_value}`; + + if (value.length <= 2 + 2 * prefixLength) { + return value; + } + + return `${value.substr(0, prefixLength)}..${value.slice(-prefixLength)}`; +} diff --git a/packages/util/src/string/toU8a.spec.ts b/packages/util/src/string/toU8a.spec.ts new file mode 100644 index 0000000000..5f906a9c99 --- /dev/null +++ b/packages/util/src/string/toU8a.spec.ts @@ -0,0 +1,21 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringToU8a } from '..'; + +describe('stringToU8a', () => { + it('decodes to an empty string for undefined', () => { + expect( + stringToU8a() + ).toEqual(new Uint8Array([])); + }); + + it('encodes the string correctly', () => { + expect( + stringToU8a('Привет, мир!') + ).toEqual( + new Uint8Array([208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 44, 32, 208, 188, 208, 184, 209, 128, 33]) + ); + }); +}); diff --git a/packages/util/src/string/toU8a.ts b/packages/util/src/string/toU8a.ts new file mode 100644 index 0000000000..99a0bf8527 --- /dev/null +++ b/packages/util/src/string/toU8a.ts @@ -0,0 +1,45 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +let encoder: { encode: (value: string) => Uint8Array }; + +function polyfilledEncode (value: string): Uint8Array { + const u8a = new Uint8Array(value.length); + + for (let i = 0; i < value.length; i++) { + u8a[i] = value.charCodeAt(i); + } + + return u8a; +} + +try { + encoder = new TextEncoder(); +} catch (error) { + encoder = { + encode: polyfilledEncode + }; +} + +/** + * @name stringToU8a + * @summary Creates a Uint8Array object from a utf-8 string. + * @description + * String input values return the actual encoded `UInt8Array`. `null` or `undefined` values returns an empty encoded array. + * @example + *
+ * + * ```javascript + * import { stringToU8a } from '@plugnet/util'; + * + * stringToU8a('hello'); // [0x68, 0x65, 0x6c, 0x6c, 0x6f] + * ``` + */ +export default function stringToU8a (value?: string): Uint8Array { + if (!value) { + return new Uint8Array([]); + } + + return encoder.encode(value); +} diff --git a/packages/util/src/string/upperFirst.spec.ts b/packages/util/src/string/upperFirst.spec.ts new file mode 100644 index 0000000000..ba6920ccff --- /dev/null +++ b/packages/util/src/string/upperFirst.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { stringUpperFirst } from '.'; + +describe('stringUpperFirst', () => { + it("uppers the first letter if it's a capital letter", () => { + expect( + stringUpperFirst('ABC') + ).toBe('ABC'); + }); + + it("uppers the first letter if it's a lowercase letter", () => { + expect( + stringUpperFirst('abc') + ).toBe('Abc'); + }); + + it('returns undefined as empty', () => { + expect( + stringUpperFirst() + ).toBe(''); + }); + + it('returns null as empty', () => { + expect( + stringUpperFirst(null) + ).toBe(''); + }); +}); diff --git a/packages/util/src/string/upperFirst.ts b/packages/util/src/string/upperFirst.ts new file mode 100644 index 0000000000..f9d106ffb5 --- /dev/null +++ b/packages/util/src/string/upperFirst.ts @@ -0,0 +1,25 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name stringUpperFirst + * @summary Lowercase the first letter of a string + * @description + * Lowercase the first letter of a string + * @example + *
+ * + * ```javascript + * import { stringUpperFirst } from '@plugnet/util'; + * + * stringUpperFirst('abc'); // => 'Abc' + * ``` + */ +export default function stringUpperFirst (value?: string | null): string { + if (!value) { + return ''; + } + + return value.charAt(0).toUpperCase() + value.slice(1); +} diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts new file mode 100644 index 0000000000..3dd36e1a1b --- /dev/null +++ b/packages/util/src/types.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +export interface ExtErrorInterface { + code: number; + data: any; + message: string; + stack: string; +} + +type Logger$Data$Fn = () => Array; +export type Logger$Data = Array; + +export type Logger = { + debug: (...values: Logger$Data) => void, + error: (...values: Logger$Data) => void, + log: (...values: Logger$Data) => void, + noop: (...values: Logger$Data) => void, + warn: (...values: Logger$Data) => void +}; + +export interface ToBnOptions { + isLe?: boolean; + isNegative?: boolean; +} diff --git a/packages/util/src/u8a/concat.spec.ts b/packages/util/src/u8a/concat.spec.ts new file mode 100644 index 0000000000..4665fbb444 --- /dev/null +++ b/packages/util/src/u8a/concat.spec.ts @@ -0,0 +1,31 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aConcat } from '.'; + +describe('u8aConcat', () => { + it('concatenates arrays', () => { + expect( + u8aConcat( + new Uint8Array([1, 2, 3, 4]), + new Uint8Array([5, 6]), + new Uint8Array([7, 8, 9]) + ) + ).toEqual( + new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + ); + }); + + it('concatenates arrays & hex values', () => { + expect( + u8aConcat( + new Uint8Array([1, 2, 3, 4]), + '0x0506', + '0x070809' + ) + ).toEqual( + new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]) + ); + }); +}); diff --git a/packages/util/src/u8a/concat.ts b/packages/util/src/u8a/concat.ts new file mode 100644 index 0000000000..c6b1a75829 --- /dev/null +++ b/packages/util/src/u8a/concat.ts @@ -0,0 +1,36 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import u8aToU8a from './toU8a'; + +/** + * @name u8aConcat + * @summary Creates a concatenated Uint8Array from the inputs. + * @description + * Concatenates the input arrays into a single `UInt8Array`. + * @example + *
+ * + * ```javascript + * import { u8aConcat } from '@plugnet/util'; + * + * u8aConcat( + * new Uint8Array([1, 2, 3]), + * new Uint8Array([4, 5, 6]) + * ); // [1, 2, 3, 4, 5, 6] + * ``` + */ +export default function u8aConcat (..._list: Array): Uint8Array { + const list: Array = _list.map(u8aToU8a); + const length = list.reduce((total, item) => total + item.length, 0); + const result = new Uint8Array(length); + let offset = 0; + + return list.reduce((result, item) => { + result.set(item, offset); + offset += item.length; + + return result; + }, result); +} diff --git a/packages/util/src/u8a/fixLength.spec.ts b/packages/util/src/u8a/fixLength.spec.ts new file mode 100644 index 0000000000..4b2c435a2b --- /dev/null +++ b/packages/util/src/u8a/fixLength.spec.ts @@ -0,0 +1,33 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aFixLength } from '.'; + +describe('u8aFixLength', () => { + it('returns bitLength === -1 as-is', () => { + expect( + u8aFixLength( + new Uint8Array([0x12, 0x34, 0x56, 0x78]) + ) + ).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78])); + }); + + it('does not change when bitlength === length', () => { + expect( + u8aFixLength(new Uint8Array([0x12, 0x34, 0x56, 0x78]), 32) + ).toEqual(new Uint8Array([0x12, 0x34, 0x56, 0x78])); + }); + + it('trims values when bitLength > length', () => { + expect( + u8aFixLength(new Uint8Array([0x12, 0x34, 0x56, 0x78]), 16) + ).toEqual(new Uint8Array([0x12, 0x34])); + }); + + it('adds zeros when bitLength < length (withPadded)', () => { + expect( + u8aFixLength(new Uint8Array([0x12, 0x34]), 32) + ).toEqual(new Uint8Array([0, 0, 0x12, 0x34])); + }); +}); diff --git a/packages/util/src/u8a/fixLength.ts b/packages/util/src/u8a/fixLength.ts new file mode 100644 index 0000000000..00e6b10319 --- /dev/null +++ b/packages/util/src/u8a/fixLength.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name u8aFixLength + * @summary Shifts a Uint8Array to a specific bitLength + * @description + * Returns a uint8Array with the specified number of bits contained in the return value. (If bitLength is -1, length checking is not done). Values with more bits are trimmed to the specified length. + * @example + *
+ * + * ```javascript + * import { u8aFixLength } from '@plugnet/util'; + * + * u8aFixLength('0x12') // => 0x12 + * u8aFixLength('0x12', 16) // => 0x0012 + * u8aFixLength('0x1234', 8) // => 0x12 + * ``` + */ +export default function u8aFixLength (value: Uint8Array, bitLength: number = -1, atStart: boolean = false): Uint8Array { + const byteLength = Math.ceil(bitLength / 8); + + if (bitLength === -1 || value.length === byteLength) { + return value; + } + + if (value.length > byteLength) { + return value.subarray(0, byteLength); + } + + const result = new Uint8Array(byteLength); + + if (atStart) { + result.set(value, 0); + } else { + result.set(value, byteLength - value.length); + } + + return result; +} diff --git a/packages/util/src/u8a/index.ts b/packages/util/src/u8a/index.ts new file mode 100644 index 0000000000..2ffdab6b8e --- /dev/null +++ b/packages/util/src/u8a/index.ts @@ -0,0 +1,15 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @summary Utility methods to convert to and from `Uint8Array` objects + */ + +export { default as u8aConcat } from './concat'; +export { default as u8aFixLength } from './fixLength'; +export { default as u8aToBn } from './toBn'; +export { default as u8aToBuffer } from './toBuffer'; +export { default as u8aToHex } from './toHex'; +export { default as u8aToString } from './toString'; +export { default as u8aToU8a } from './toU8a'; diff --git a/packages/util/src/u8a/toBn.spec.ts b/packages/util/src/u8a/toBn.spec.ts new file mode 100644 index 0000000000..172a485dba --- /dev/null +++ b/packages/util/src/u8a/toBn.spec.ts @@ -0,0 +1,93 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToBn } from '.'; + +describe('u8aToBn', () => { + it('converts little-endian by default', () => { + expect( + u8aToBn( + new Uint8Array([0x12, 0x34]) + ).toString(16) + ).toBe('3412'); + }); + it('converts values (big-endian)', () => { + expect( + u8aToBn( + new Uint8Array([0x12, 0x34]), + { isLe: false } + ).toString(16) + ).toBe('1234'); + }); + + it('converts values (little-endian)', () => { + expect( + u8aToBn( + new Uint8Array([0x12, 0x34]), + { isLe: true } + ).toString(16) + ).toBe('3412'); + }); + + it('converts empty', () => { + expect( + u8aToBn( + new Uint8Array([]), + { isLe: true } + ).toString(16) + ).toBe('0'); + }); + + it('handles negative numbers (little-endian)', () => { + expect( + u8aToBn( + new Uint8Array([46, 251]), + { isLe: true, isNegative: true } + ).toNumber() + ).toBe(-1234); + }); + + it('handles negative numbers (big-endian)', () => { + expect( + u8aToBn( + new Uint8Array([251, 46]), + { isLe: false, isNegative: true } + ).toNumber() + ).toBe(-1234); + }); + + it('handles overflows correctly (little-endian)', () => { + expect( + u8aToBn( + new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0]), + { isLe: true } + ).toNumber() + ).toBe(256); + }); + + it('handles backward compatibility)', () => { + expect( + u8aToBn( + new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0]), + false + ).eq( + u8aToBn( + new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0]), + { isLe: false } + ) + ) + ).toBe(true); + expect( + u8aToBn( + new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0]), + true + ).eq( + u8aToBn( + new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0]), + { isLe: true } + ) + ) + ).toBe(true); + }); +}); diff --git a/packages/util/src/u8a/toBn.ts b/packages/util/src/u8a/toBn.ts new file mode 100644 index 0000000000..6a35fd3333 --- /dev/null +++ b/packages/util/src/u8a/toBn.ts @@ -0,0 +1,37 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import BN from 'bn.js'; + +import hexToBn from '../hex/toBn'; +import u8aToHex from './toHex'; +import { ToBnOptions } from '../types'; + +/** + * @name u8aToBn + * @summary Creates a BN from a Uint8Array object. + * @description + * `UInt8Array` input values return the actual BN. `null` or `undefined` values returns an `0x0` value. + * @param value The value to convert + * @param options Options to pass while converting + * @param options.isLe Convert using Little Endian + * @param options.isNegative Convert using two's complement + * @example + *
+ * + * ```javascript + * import { u8aToBn } from '@plugnet/util'; + * + * u8aToHex(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0xf])); // 0x68656c0f + * ``` + */ +export default function u8aToBn ( + value: Uint8Array, + options: ToBnOptions | boolean = { isLe: true, isNegative: false } +): BN { + return hexToBn( + u8aToHex(value), + options + ); +} diff --git a/packages/util/src/u8a/toBuffer.spec.ts b/packages/util/src/u8a/toBuffer.spec.ts new file mode 100644 index 0000000000..9e67ad37b4 --- /dev/null +++ b/packages/util/src/u8a/toBuffer.spec.ts @@ -0,0 +1,19 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToBuffer } from '.'; + +describe('u8aToBuffer', () => { + it('returns [] when the buffer is null', () => { + expect( + u8aToBuffer(null) + ).toEqual(Buffer.from([])); + }); + + it('returns the Buffer value for the Uint8Array', () => { + expect( + u8aToBuffer(new Uint8Array([128, 0, 10])) + ).toEqual(Buffer.from([128, 0, 10])); + }); +}); diff --git a/packages/util/src/u8a/toBuffer.ts b/packages/util/src/u8a/toBuffer.ts new file mode 100644 index 0000000000..b938f88c9f --- /dev/null +++ b/packages/util/src/u8a/toBuffer.ts @@ -0,0 +1,26 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +/** + * @name u8aToBuffer + * @summary Creates a Buffer object from a hex string. + * @description + * `null` inputs returns an empty `Buffer` result. `UInt8Array` input values return the actual bytes value converted to a `Buffer`. Anything that is not a `UInt8Array` throws an error. + * @example + *
+ * + * ```javascript + * import { u8aToBuffer } from '@plugnet/util'; + * + * console.log('Buffer', u8aToBuffer('0x123480001f')); + * ``` + */ +export default function u8aToBuffer (value?: Uint8Array | null): Buffer { + if (!value) { + return Buffer.from([]); + } + + // @ts-ignore yes, from also works with u8a + return Buffer.from(value); +} diff --git a/packages/util/src/u8a/toHex.spec.ts b/packages/util/src/u8a/toHex.spec.ts new file mode 100644 index 0000000000..17575d4aa6 --- /dev/null +++ b/packages/util/src/u8a/toHex.spec.ts @@ -0,0 +1,61 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToHex } from '.'; + +describe('u8aToHex', () => { + it('returns empty as 0x', () => { + expect( + u8aToHex() + ).toEqual('0x'); + }); + + it('returns empty as "" (unprefixed)', () => { + expect( + u8aToHex(null, -1, false) + ).toEqual(''); + }); + + it('returns the hex value for the array', () => { + expect( + u8aToHex( + new Uint8Array([128, 0, 10]) + ) + ).toEqual('0x80000a'); + }); + + it('returns the hex value for the array (unprefixed)', () => { + expect( + u8aToHex( + new Uint8Array([128, 0, 10]), + -1, false + ) + ).toEqual('80000a'); + }); + + it('handles starting zeros correctly', () => { + expect( + u8aToHex( + new Uint8Array([ 0, 1, 0, 0, 0, 0, 0, 0 ]) + ) + ).toEqual('0x0001000000000000'); + }); + + it('returns the hex value where allowed < max', () => { + expect( + u8aToHex( + new Uint8Array(Uint8Array.from([128, 0, 10, 11]), 64) + ) + ).toEqual('0x80000a0b'); + }); + + it('returns the trimmed hex value where allowed >= max', () => { + expect( + u8aToHex( + new Uint8Array([128, 0, 10, 11, 12, 13]), + 32 + ) + ).toEqual('0x8000…0c0d'); + }); +}); diff --git a/packages/util/src/u8a/toHex.ts b/packages/util/src/u8a/toHex.ts new file mode 100644 index 0000000000..b95119b77f --- /dev/null +++ b/packages/util/src/u8a/toHex.ts @@ -0,0 +1,44 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +const ALPHABET = '0123456789abcdef'; + +/** + * @name u8aToHex + * @summary Creates a hex string from a Uint8Array object. + * @description + * `UInt8Array` input values return the actual hex string. `null` or `undefined` values returns an `0x` string. + * @example + *
+ * + * ```javascript + * import { u8aToHex } from '@plugnet/util'; + * + * u8aToHex(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0xf])); // 0x68656c0f + * ``` + */ +export default function u8aToHex (value?: Uint8Array | null, bitLength: number = -1, isPrefixed: boolean = true): string { + const prefix = isPrefixed + ? '0x' + : ''; + + if (!value || !value.length) { + return prefix; + } + + const byteLength = Math.ceil(bitLength / 8); + + if (byteLength > 0 && value.length > byteLength) { + const halfLength = Math.ceil(byteLength / 2); + + return `${u8aToHex(value.subarray(0, halfLength), -1, isPrefixed)}…${u8aToHex(value.subarray(value.length - halfLength), -1, false)}`; + } + + // based on comments in https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex and + // implementation in http://jsben.ch/Vjx2V - optimisation here suggests that a forEach loop is faster + // than reduce as well (clocking at in 90% of the reduce speed with tweaking in the playpen above) + return value.reduce((result, value) => { + return result + ALPHABET[value >> 4] + ALPHABET[value & 15]; + }, prefix); +} diff --git a/packages/util/src/u8a/toString.spec.ts b/packages/util/src/u8a/toString.spec.ts new file mode 100644 index 0000000000..c0ebc9d3c0 --- /dev/null +++ b/packages/util/src/u8a/toString.spec.ts @@ -0,0 +1,27 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToString } from '..'; + +describe('u8aToString', () => { + it('decodes to an empty string for undefined', () => { + expect( + u8aToString() + ).toEqual(''); + }); + + it('decodes to an empty string for empty buffer', () => { + expect( + u8aToString(new Uint8Array([])) + ).toEqual(''); + }); + + it('decodes the buffer correctly', () => { + expect( + u8aToString( + new Uint8Array([208, 159, 209, 128, 208, 184, 208, 178, 208, 181, 209, 130, 44, 32, 208, 188, 208, 184, 209, 128, 33]) + ) + ).toEqual('Привет, мир!'); + }); +}); diff --git a/packages/util/src/u8a/toString.ts b/packages/util/src/u8a/toString.ts new file mode 100644 index 0000000000..e8467a2626 --- /dev/null +++ b/packages/util/src/u8a/toString.ts @@ -0,0 +1,41 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +let decoder: { decode: (value: Uint8Array) => string }; + +function polyfilledDecode (value: Uint8Array): string { + return value.reduce((str, code) => { + return str + String.fromCharCode(code); + }, ''); +} + +try { + decoder = new TextDecoder('utf-8'); +} catch (error) { + decoder = { + decode: polyfilledDecode + }; +} + +/** + * @name u8aToString + * @summary Creates a utf-8 string from a Uint8Array object. + * @description + * `UInt8Array` input values return the actual decoded utf-8 string. `null` or `undefined` values returns an empty string. + * @example + *
+ * + * ```javascript + * import { u8aToString } from '@plugnet/util'; + * + * u8aToString(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f])); // hello + * ``` + */ +export default function u8aToString (value?: Uint8Array | null): string { + if (!value || !value.length) { + return ''; + } + + return decoder.decode(value); +} diff --git a/packages/util/src/u8a/toU8a.spec.ts b/packages/util/src/u8a/toU8a.spec.ts new file mode 100644 index 0000000000..df4eed5a61 --- /dev/null +++ b/packages/util/src/u8a/toU8a.spec.ts @@ -0,0 +1,51 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import { u8aToU8a } from '.'; + +describe('u8aToU8a', () => { + it('returns an empty Uint8Array when null provided', () => { + expect( + u8aToU8a(null) + ).toHaveLength(0); + }); + + it('returns a Uint8Array (hex input)', () => { + expect( + u8aToU8a('0x80000a') + ).toEqual( + new Uint8Array([128, 0, 10]) + ); + }); + + it('returns Uint8Array (string input)', () => { + expect( + u8aToU8a('abcde fghij') + ).toEqual(new Uint8Array([97, 98, 99, 100, 101, 32, 102, 103, 104, 105, 106])); + }); + + it('returns a Uint8Array (buffer input)', () => { + expect( + u8aToU8a(Buffer.from('80000a', 'hex')) + ).toEqual( + new Uint8Array([128, 0, 10]) + ); + }); + + it('creates from Array', () => { + expect( + u8aToU8a([128, 0, 10, 11, 12]) + ).toEqual( + new Uint8Array([128, 0, 10, 11, 12]) + ); + }); + + it('returns a Uint8Array as-is (u8a input)', () => { + expect( + u8aToU8a(new Uint8Array([128, 0, 10])) + ).toEqual( + new Uint8Array([128, 0, 10]) + ); + }); +}); diff --git a/packages/util/src/u8a/toU8a.ts b/packages/util/src/u8a/toU8a.ts new file mode 100644 index 0000000000..1224013c3c --- /dev/null +++ b/packages/util/src/u8a/toU8a.ts @@ -0,0 +1,47 @@ +// Copyright 2017-2019 @polkadot/util authors & contributors +// This software may be modified and distributed under the terms +// of the Apache-2.0 license. See the LICENSE file for details. + +import isBuffer from '../is/buffer'; +import isHex from '../is/hex'; +import isString from '../is/string'; +import bufferToU8a from '../buffer/toU8a'; +import hexToU8a from '../hex/toU8a'; +import stringToU8a from '../string/toU8a'; + +/** + * @name u8aToU8a + * @summary Creates a Uint8Array value from a Uint8Array, Buffer, string or hex input. + * @description + * `null` ior `undefined` nputs returns a `[]` result, Uint8Array values returns the value, hex strings returns a Uint8Array representation. + * @example + *
+ * + * ```javascript + * import { u8aToU8a } from '@plugnet/util'; + * + * u8aToU8a(new Uint8Array([0x12, 0x34]); // => Uint8Array([0x12, 0x34]) + * u8aToU8a(0x1234); // => Uint8Array([0x12, 0x34]) + * ``` + */ +export default function u8aToU8a (value?: Array | Buffer | Uint8Array | string | null): Uint8Array { + if (!value) { + return new Uint8Array(); + } + + if (isBuffer(value)) { + return bufferToU8a(value); + } + + if (isString(value)) { + return isHex(value) + ? hexToU8a(value) + : stringToU8a(value); + } + + if (Array.isArray(value)) { + return Uint8Array.from(value); + } + + return value; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..1daa4279ee --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,40 @@ +{ + "extends": "./node_modules/@plugnet/dev/config/tsconfig", + "compilerOptions": + { + "baseUrl": ".", + "paths": + { + "@plugnet/api": ["packages/api/src"], + "@plugnet/api/*": ["packages/api/src/*"], + "@plugnet/api-derive": ["packages/api-derive/src"], + "@plugnet/api-derive/*": ["packages/api-derive/src/*"], + "@plugnet/rpc-core": ["packages/rpc-core/src"], + "@plugnet/rpc-core/*": ["packages/rpc-core/src/*"], + "@plugnet/rpc-provider": ["packages/rpc-provider/src"], + "@plugnet/rpc-provider/*": ["packages/rpc-provider/src/*"], + "@plugnet/rpc-rx": ["packages/rpc-rx/src"], + "@plugnet/rpc-rx/*": ["packages/rpc-rx/src/*"], + "@plugnet/extrinsics": ["packages/type-extrinsics/src"], + "@plugnet/extrinsics/*": ["packages/type-extrinsics/src/*"], + "@plugnet/jsonrpc": ["packages/type-jsonrpc/src"], + "@plugnet/jsonrpc/*": ["packages/type-jsonrpc/src/*"], + "@plugnet/storage": ["packages/type-storage/src"], + "@plugnet/storage/*": ["packages/type-storage/src/*"], + "@plugnet/types": ["packages/types/src"], + "@plugnet/types/*": ["packages/types/src/*"], + "@plugnet/keyring": ["packages/keyring/src"], + "@plugnet/keyring/*": ["packages/keyring/src/*"], + "@plugnet/util": ["packages/util/src"], + "@plugnet/util/*": ["packages/util/src/*"], + "@plugnet/util-crypto": ["packages/util-crypto/src"], + "@plugnet/util-crypto/*": ["packages/util-crypto/src/*"] + }, + "typeRoots": [ + "./node_modules/@polkadot/ts", + "./node_modules/@types" + ], + // FIXME This is a HACK that is needed for the current mock-socket defs + "skipLibCheck": true + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000000..e2a507d151 --- /dev/null +++ b/tslint.json @@ -0,0 +1,9 @@ +{ + "extends": "@polkadot/dev/config/tslint", + "linterOptions": { + "exclude": "packages/**/src/**/*.json" + }, + "rules": { + "completed-docs": false + } +} diff --git a/typedoc.js b/typedoc.js new file mode 100644 index 0000000000..c7047287f2 --- /dev/null +++ b/typedoc.js @@ -0,0 +1,16 @@ +module.exports = { + name: 'Polkadot JS API', + exclude: '**/*+(index|e2e|spec).ts', + excludeExternals: true, + excludeNotExported: true, + excludeProtected: true, + excludePrivate: true, + hideGenerator: true, + includeDeclarations: false, + out: 'docs', + module: 'commonjs', + moduleResolution: 'node', + mdEngine: 'gitbook', + stripInternal: 'true', + theme: 'markdown' +};