diff --git a/api/build.gradle b/api/build.gradle index 44dc6ea0..259cf626 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -18,6 +18,7 @@ dependencies { testImplementation 'org.testcontainers:testcontainers:1.17.2' testImplementation 'org.testcontainers:junit-jupiter:1.17.2' testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.hamcrest:hamcrest:2.2' testAnnotationProcessor project(':pallet:pallet-codegen') } diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java b/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java index 81153c84..d9206626 100644 --- a/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java +++ b/api/src/main/java/com/strategyobject/substrateclient/api/DefaultModule.java @@ -3,9 +3,16 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; +import com.strategyobject.substrateclient.api.pallet.balances.AccountData; +import com.strategyobject.substrateclient.api.pallet.balances.AccountDataReader; +import com.strategyobject.substrateclient.api.pallet.system.System; +import com.strategyobject.substrateclient.common.types.Lambda; import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.pallet.GeneratedPalletFactory; import com.strategyobject.substrateclient.pallet.PalletFactory; +import com.strategyobject.substrateclient.pallet.events.EventDescriptor; +import com.strategyobject.substrateclient.pallet.events.EventDescriptorReader; +import com.strategyobject.substrateclient.pallet.events.EventRegistry; import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory; import com.strategyobject.substrateclient.rpc.RpcSectionFactory; import com.strategyobject.substrateclient.rpc.api.section.State; @@ -35,12 +42,12 @@ public class DefaultModule extends AbstractModule { private final @NonNull ProviderInterface providerInterface; - private Consumer configureScaleReaderRegistry = x -> x.registerAnnotatedFrom(PREFIX); - private Consumer configureScaleWriterRegistry = x -> x.registerAnnotatedFrom(PREFIX); - private BiConsumer configureRpcDecoderRegistry = - (registry, contextFactory) -> registry.registerAnnotatedFrom(contextFactory, PREFIX); - private BiConsumer configureRpcEncoderRegistry = - (registry, contextFactory) -> registry.registerAnnotatedFrom(contextFactory, PREFIX); + private Consumer configureScaleReaderRegistry = Lambda::noop; + private Consumer configureScaleWriterRegistry = Lambda::noop; + private BiConsumer configureRpcDecoderRegistry = Lambda::noop; + private BiConsumer configureRpcEncoderRegistry = Lambda::noop; + private Consumer configureEventRegistry = Lambda::noop; + public DefaultModule configureScaleReaderRegistry(Consumer configure) { configureScaleReaderRegistry = configureScaleReaderRegistry.andThen(configure); @@ -62,6 +69,11 @@ public DefaultModule configureRpcEncoderRegistry(BiConsumer configure) { + configureEventRegistry = configureEventRegistry.andThen(configure); + return this; + } + @Override protected void configure() { try { @@ -97,8 +109,13 @@ protected void configure() { @Provides @Singleton - public ScaleReaderRegistry provideScaleReaderRegistry() { + public ScaleReaderRegistry provideScaleReaderRegistry(MetadataProvider metadataProvider, + EventRegistry eventRegistry) { val registry = new ScaleReaderRegistry(); + registry.registerAnnotatedFrom(PREFIX); + registry.register(new AccountDataReader(registry), System.AccountData.class, AccountData.class); + registry.register(new EventDescriptorReader(registry, metadataProvider, eventRegistry), EventDescriptor.class); + configureScaleReaderRegistry.accept(registry); return registry; } @@ -107,6 +124,8 @@ public ScaleReaderRegistry provideScaleReaderRegistry() { @Singleton public ScaleWriterRegistry provideScaleWriterRegistry() { val registry = new ScaleWriterRegistry(); + registry.registerAnnotatedFrom(PREFIX); + configureScaleWriterRegistry.accept(registry); return registry; } @@ -120,6 +139,7 @@ public RpcDecoderRegistry provideRpcDecoderRegistry(MetadataProvider metadataPro metadataProvider, registry, scaleReaderRegistry); + registry.registerAnnotatedFrom(() -> context, PREFIX); configureRpcDecoderRegistry.accept(registry, () -> context); return registry; @@ -134,11 +154,22 @@ public RpcEncoderRegistry provideRpcEncoderRegistry(MetadataProvider metadataPro metadataProvider, registry, scaleWriterRegistry); + registry.registerAnnotatedFrom(() -> context, PREFIX); configureRpcEncoderRegistry.accept(registry, () -> context); return registry; } + @Provides + @Singleton + public EventRegistry provideEventRegistry() { + val registry = new EventRegistry(); + registry.registerAnnotatedFrom(PREFIX); + + configureEventRegistry.accept(registry); + return registry; + } + @Provides @Singleton public MetadataProvider provideMetadata() { diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/AccountData.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/AccountData.java new file mode 100644 index 00000000..6ee56392 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/AccountData.java @@ -0,0 +1,27 @@ +package com.strategyobject.substrateclient.api.pallet.balances; + +import com.strategyobject.substrateclient.api.pallet.system.System; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigInteger; + +@ScaleReader +@Getter +@Setter +public class AccountData implements System.AccountData { + @Scale(ScaleType.U128.class) + private BigInteger free; + + @Scale(ScaleType.U128.class) + private BigInteger reserved; + + @Scale(ScaleType.U128.class) + private BigInteger miscFrozen; + + @Scale(ScaleType.U128.class) + private BigInteger feeFrozen; +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/Balances.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/Balances.java new file mode 100644 index 00000000..33950f7b --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/balances/Balances.java @@ -0,0 +1,118 @@ +package com.strategyobject.substrateclient.api.pallet.balances; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.BalanceStatus; +import com.strategyobject.substrateclient.rpc.api.primitives.Balance; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Balances") +public interface Balances { + + /** + * An account was created with some free balance. \[account, free_balance\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class Endowed { + private AccountId account; + private Balance freeBalance; + } + + /** + * An account was removed whose balance was non-zero but below ExistentialDeposit, + * resulting in an outright loss. \[account, balance\] + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class DustLost { + private AccountId account; + private Balance balance; + } + + /** + * Transfer succeeded. \[from, to, value\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class Transfer { + private AccountId from; + private AccountId to; + private Balance value; + } + + /** + * A balance was set by root. \[who, free, reserved\] + */ + @Event(index = 3) + @Getter + @Setter + @ScaleReader + class BalanceSet { + private AccountId account; + private Balance free; + private Balance reserved; + } + + /** + * Some amount was deposited (e.g. for transaction fees). \[who, deposit\] + */ + @Event(index = 4) + @Getter + @Setter + @ScaleReader + class Deposit { + private AccountId account; + private Balance value; + } + + /** + * Some balance was reserved (moved from free to reserved). \[who, value\] + */ + @Event(index = 5) + @Getter + @Setter + @ScaleReader + class Reserved { + private AccountId account; + private Balance value; + } + + /** + * Some balance was unreserved (moved from reserved to free). \[who, value\] + */ + @Event(index = 6) + @Getter + @Setter + @ScaleReader + class Unreserved { + private AccountId account; + private Balance value; + } + + /** + * Some balance was moved from the reserve of the first account to the second account. + * Final argument indicates the destination balance type. + * \[from, to, balance, destination_status\] + */ + @Event(index = 7) + @Getter + @Setter + @ScaleReader + class ReserveRepatriated { + private AccountId from; + private AccountId to; + private Balance value; + private BalanceStatus destinationStatus; + } + +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Authority.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Authority.java new file mode 100644 index 00000000..9b504f89 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Authority.java @@ -0,0 +1,20 @@ +package com.strategyobject.substrateclient.api.pallet.grandpa; + +import com.strategyobject.substrateclient.crypto.PublicKey; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigInteger; + +@ScaleReader +@Getter +@Setter +public class Authority { + private PublicKey authorityId; + + @Scale(ScaleType.U64.class) + private BigInteger authorityWeight; +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Grandpa.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Grandpa.java new file mode 100644 index 00000000..86d3b1c0 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/grandpa/Grandpa.java @@ -0,0 +1,41 @@ +package com.strategyobject.substrateclient.api.pallet.grandpa; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Pallet("Grandpa") +public interface Grandpa { + + /** + * New authority set has been applied. \[authority_set\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class NewAuthorities { + private List authorityList; + } + + /** + * Current authority set has been paused. + */ + @Event(index = 1) + @ScaleReader + class Paused { + } + + /** + * Current authority set has been resumed. + */ + @Event(index = 2) + @ScaleReader + class Resumed { + } + +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/indices/Indices.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/indices/Indices.java new file mode 100644 index 00000000..19904f07 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/indices/Indices.java @@ -0,0 +1,48 @@ +package com.strategyobject.substrateclient.api.pallet.indices; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.primitives.AccountIndex; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Indices") +public interface Indices { + + /** + * An account index was assigned. \[index, who\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class IndexAssigned { + private AccountId account; + private AccountIndex index; + } + + /** + * An account index has been freed up (unassigned). \[index\] + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class IndexFreed { + private AccountIndex accountIndex; + } + + /** + * An account index has been frozen to its current account ID. \[index, who\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class IndexFrozen { + private AccountIndex index; + private AccountId account; + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/Proxy.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/Proxy.java new file mode 100644 index 00000000..3a1142fc --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/Proxy.java @@ -0,0 +1,74 @@ +package com.strategyobject.substrateclient.api.pallet.proxy; + +import com.strategyobject.substrateclient.common.types.Result; +import com.strategyobject.substrateclient.common.types.Unit; +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.CallHash; +import com.strategyobject.substrateclient.rpc.api.runtime.DispatchError; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Proxy") +public interface Proxy { + + /** + * A proxy was executed correctly, with the given \[result\]. + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class ProxyExecuted { + private Result dispatchResult; + } + + /** + * Anonymous account has been created by new proxy with given + * disambiguation index and proxy type. \[anonymous, who, proxy_type, + * disambiguation_index\] + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class AnonymousCreated { + private AccountId anonymous; + private AccountId who; + private ProxyType proxyType; + @Scale(ScaleType.U16.class) + private Integer disambiguationIndex; + } + + /** + * An announcement was placed to make a call in the future. \[real, proxy, call_hash\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class Announced { + private AccountId real; + private AccountId proxy; + private CallHash callHash; + } + + /** + * A proxy was added. \[delegator, delegatee, proxy_type, delay\] + */ + @Event(index = 3) + @Getter + @Setter + @ScaleReader + class ProxyAdded { + private AccountId delegator; + private AccountId delegatee; + private ProxyType proxyType; + private BlockNumber blockNumber; + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/ProxyType.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/ProxyType.java new file mode 100644 index 00000000..12c6c721 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/proxy/ProxyType.java @@ -0,0 +1,14 @@ +package com.strategyobject.substrateclient.api.pallet.proxy; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * The type used to represent the kinds of proxying allowed. + */ +@ScaleReader +public enum ProxyType { + ANY, + NON_TRANSFER, + GOVERNANCE, + STAKING +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/session/Session.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/session/Session.java new file mode 100644 index 00000000..f085f5be --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/session/Session.java @@ -0,0 +1,27 @@ +package com.strategyobject.substrateclient.api.pallet.session; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Session") +public interface Session { + + /** + * New session has happened. Note that the argument is the \[session_index\], not the + * block number as the type might suggest. + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class NewSession { + @Scale(ScaleType.U32.class) + private Long sessionIndex; + } + +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/staking/Staking.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/staking/Staking.java new file mode 100644 index 00000000..0f6bb1c6 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/staking/Staking.java @@ -0,0 +1,161 @@ +package com.strategyobject.substrateclient.api.pallet.staking; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.primitives.Balance; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Staking") +public interface Staking { + + /** + * The era payout has been set; the first balance is the validator-payout; the second is + * the remainder from the maximum amount of reward. + * \[era_index, validator_payout, remainder\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class EraPaid { + @Scale(ScaleType.U32.class) + private Long eraIndex; + private Balance payout; + private Balance remainder; + } + + /** + * The nominator has been rewarded by this amount. \[stash, amount\] + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class Rewarded { + private AccountId stash; + private Balance amount; + } + + /** + * One validator (and its nominators) has been slashed by the given amount. + * \[validator, amount\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class Slashed { + private AccountId validator; + private Balance amount; + } + + /** + * An old slashing report from a prior era was discarded because it could + * not be processed. \[session_index\] + */ + @Event(index = 3) + @Getter + @Setter + @ScaleReader + class OldSlashingReportDiscarded { + @Scale(ScaleType.U32.class) + private Long sessionIndex; + } + + /** + * A new set of stakers was elected. + */ + @Event(index = 4) + @ScaleReader + class StakersElected { + } + + /** + * An account has bonded this amount. \[stash, amount\] + *

+ * NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably, + * it will not be emitted for staking rewards when they are added to stake. + */ + @Event(index = 5) + @Getter + @Setter + @ScaleReader + class Bonded { + private AccountId stash; + private Balance amount; + } + + /** + * An account has unbonded this amount. \[stash, amount\] + */ + @Event(index = 6) + @Getter + @Setter + @ScaleReader + class Unbonded { + private AccountId stash; + private Balance amount; + } + + /** + * An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance` + * from the unlocking queue. \[stash, amount\] + */ + @Event(index = 7) + @Getter + @Setter + @ScaleReader + class Withdrawn { + private AccountId stash; + private Balance amount; + } + + /** + * A nominator has been kicked from a validator. \[nominator, stash\] + */ + @Event(index = 8) + @Getter + @Setter + @ScaleReader + class Kicked { + private AccountId nominator; + private AccountId stash; + } + + /** + * The election failed. No new era is planned. + */ + @Event(index = 9) + @ScaleReader + class StakingElectionFailed { + } + + /** + * An account has stopped participating as either a validator or nominator. \[stash\] + */ + @Event(index = 10) + @Getter + @Setter + @ScaleReader + class Chilled { + private AccountId stash; + } + + /** + * The stakers' rewards are getting paid. \[era_index, validator_stash\] + */ + @Event(index = 11) + @Getter + @Setter + @ScaleReader + class PayoutStarted { + @Scale(ScaleType.U32.class) + private Long eraIndex; + private AccountId validatorStash; + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/sudo/Sudo.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/sudo/Sudo.java new file mode 100644 index 00000000..95b00111 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/sudo/Sudo.java @@ -0,0 +1,49 @@ +package com.strategyobject.substrateclient.api.pallet.sudo; + +import com.strategyobject.substrateclient.common.types.Result; +import com.strategyobject.substrateclient.common.types.Unit; +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.runtime.DispatchError; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Sudo") +public interface Sudo { + + /** + * A sudo just took place. \[result\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class Sudid { + private Result dispatchResult; + } + + /** + * The \[sudoer\] just switched identity; the old key is supplied. + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class KeyChanged { + private AccountId sudoer; + } + + /** + * A sudo just took place. \[result\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class SudoAsDone { + private Result dispatchResult; + } + +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/AccountInfo.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/AccountInfo.java new file mode 100644 index 00000000..b5a8478e --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/AccountInfo.java @@ -0,0 +1,24 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@ScaleReader +public +class AccountInfo { + @Scale(ScaleType.U32.class) + private Long nonce; + + @Scale(ScaleType.U32.class) + private Long consumers; + + @Scale(ScaleType.U32.class) + private Long providers; + + private System.AccountData data; +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/EventRecord.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/EventRecord.java new file mode 100644 index 00000000..f36635fa --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/EventRecord.java @@ -0,0 +1,30 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.strategyobject.substrateclient.pallet.events.EventDescriptor; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * Record of an event happening. + */ +@Getter +@Setter +@ScaleReader +public class EventRecord { + /** + * Record of an event happening. + */ + private Phase phase; + /** + * The event descriptor holding event description data and the event itself. + */ + private EventDescriptor event; + /** + * The list of the topics this event has. + */ + private List topics; +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/Phase.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/Phase.java new file mode 100644 index 00000000..b0a677c1 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/Phase.java @@ -0,0 +1,61 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.common.types.union.Union; + +/** + * A phase of a block's execution. + */ +public class Phase extends Union { + + private Phase() { + } + + /** + * @return true if applying an extrinsic + */ + public boolean isApplyExtrinsic() { + return index == 0; + } + + /** + * @return true if finalizing the block + */ + public boolean isFinalization() { + return index == 1; + } + + /** + * @return true if initializing the block + */ + public boolean isInitialization() { + return index == 2; + } + + /** + * @return index of the extrinsic applied + */ + public long getApplyExtrinsicIndex() { + Preconditions.checkState(index == 0); + return (long) value; + } + + public static Phase ofApplyExtrinsic(long extrinsicIndex) { + Phase result = new Phase(); + result.value = extrinsicIndex; + result.index = 0; + return result; + } + + public static Phase ofFinalization() { + Phase result = new Phase(); + result.index = 1; + return result; + } + + public static Phase ofInitialization() { + Phase result = new Phase(); + result.index = 2; + return result; + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/PhaseReader.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/PhaseReader.java new file mode 100644 index 00000000..031bcf63 --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/PhaseReader.java @@ -0,0 +1,36 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.readers.union.BaseUnionReader; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.NonNull; +import lombok.val; + +import java.io.IOException; +import java.io.InputStream; + +@AutoRegister(types = Phase.class) +public class PhaseReader extends BaseUnionReader { + private final ScaleReaderRegistry registry; + + public PhaseReader(ScaleReaderRegistry registry) { + super(3, + x -> Phase.ofApplyExtrinsic((Long) x), + x -> Phase.ofFinalization(), + x -> Phase.ofInitialization()); + + this.registry = registry; + } + + @Override + public Phase read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { + Preconditions.checkArgument(readers == null || readers.length == 0); + + val u32Reader = registry.resolve(ScaleType.U32.class); + val voidReader = registry.resolve(Void.class); + return super.read(stream, u32Reader, voidReader, voidReader); + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/System.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/System.java new file mode 100644 index 00000000..c1a10f2f --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/system/System.java @@ -0,0 +1,109 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.strategyobject.substrateclient.common.types.Into; +import com.strategyobject.substrateclient.pallet.annotation.*; +import com.strategyobject.substrateclient.pallet.storage.StorageNMap; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; +import com.strategyobject.substrateclient.rpc.api.runtime.DispatchError; +import com.strategyobject.substrateclient.rpc.api.weights.DispatchInfo; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * The System pallet provides low-level access to core types and cross-cutting utilities. + * It acts as the base layer for other pallets to interact with the Substrate framework components. + */ +@Pallet("System") +public interface System { + interface AccountData extends Into { + } + + /** + * @return The full account information for a particular account ID. + */ + @Storage( + name = "Account", + keys = { + @StorageKey( + type = @Scale(AccountId.class), + hasher = StorageHasher.BLAKE2_128_CONCAT + ) + }) + StorageNMap account(); + + /** + * @return Events deposited for the current block. + */ + @Storage(name = "Events") + StorageNMap> events(); + + /** + * An extrinsic completed successfully. + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class ExtrinsicSuccess { + private DispatchInfo dispatchInfo; + } + + /** + * An extrinsic failed. + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class ExtrinsicFailedEvent { + private DispatchError dispatchError; + private DispatchInfo dispatchInfo; + } + + /** + * `:code` was updated. + */ + @Event(index = 2) + @ScaleReader + class CodeUpdatedEvent { + } + + /** + * A new account was created. + */ + @Event(index = 3) + @Getter + @Setter + @ScaleReader + class NewAccountEvent { + private AccountId account; + } + + /** + * An account was reaped. + */ + @Event(index = 4) + @Getter + @Setter + @ScaleReader + class KilledAccountEvent { + private AccountId account; + } + + /** + * On on-chain remark happened. + */ + @Event(index = 5) + @Getter + @Setter + @ScaleReader + class RemarkedEvent { + private AccountId sender; + private Hash hash; + } +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/treasury/Treasury.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/treasury/Treasury.java new file mode 100644 index 00000000..0e58cecf --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/treasury/Treasury.java @@ -0,0 +1,100 @@ +package com.strategyobject.substrateclient.api.pallet.treasury; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.rpc.api.primitives.Balance; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Treasury") +public interface Treasury { + + /** + * New proposal. \[proposal_index\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class Proposed { + @Scale(ScaleType.U32.class) + private Long proposalIndex; + } + + /** + * We have ended a spend period and will now allocate funds. \[budget_remaining\] + */ + @Event(index = 1) + @Getter + @Setter + @ScaleReader + class Spending { + private Balance budgetRemaining; + } + + /** + * Some funds have been allocated. \[proposal_index, award, beneficiary\] + */ + @Event(index = 2) + @Getter + @Setter + @ScaleReader + class Awarded { + @Scale(ScaleType.U32.class) + private Long proposalIndex; + private Balance award; + private AccountId beneficiary; + } + + /** + * A proposal was rejected; funds were slashed. \[proposal_index, slashed\] + */ + @Event(index = 3) + @Getter + @Setter + @ScaleReader + class Rejected { + @Scale(ScaleType.U32.class) + private Long proposalIndex; + private Balance slashed; + } + + /** + * Some of our funds have been burnt. \[burn\] + */ + @Event(index = 4) + @Getter + @Setter + @ScaleReader + class Burnt { + private Balance value; + } + + /** + * Spending has finished; this is the amount that rolls over until next spend. + * \[budget_remaining\] + */ + @Event(index = 5) + @Getter + @Setter + @ScaleReader + class Rollover { + private Balance budgetRemaining; + } + + /** + * Some funds have been deposited. \[deposit\] + */ + @Event(index = 6) + @Getter + @Setter + @ScaleReader + class Deposit { + private Balance value; + } + +} diff --git a/api/src/main/java/com/strategyobject/substrateclient/api/pallet/utility/Utility.java b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/utility/Utility.java new file mode 100644 index 00000000..3717d25e --- /dev/null +++ b/api/src/main/java/com/strategyobject/substrateclient/api/pallet/utility/Utility.java @@ -0,0 +1,45 @@ +package com.strategyobject.substrateclient.api.pallet.utility; + +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import com.strategyobject.substrateclient.rpc.api.runtime.DispatchError; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +@Pallet("Utility") +public interface Utility { + + /** + * Batch of dispatches did not complete fully. Index of first failing dispatch given, as + * well as the error. \[index, error\] + */ + @Event(index = 0) + @Getter + @Setter + @ScaleReader + class BatchInterrupted { + @Scale(ScaleType.U32.class) + private Long index; + private DispatchError dispatchError; + } + + /** + * Batch of dispatches completed fully with no error. + */ + @Event(index = 1) + @ScaleReader + class BatchCompleted { + } + + /** + * A single item within a Batch of dispatches has completed with no error. + */ + @Event(index = 2) + @ScaleReader + class ItemCompleted { + } + +} diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java b/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java index f9bbcf02..704b53c8 100644 --- a/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java +++ b/api/src/test/java/com/strategyobject/substrateclient/api/ApiTest.java @@ -6,8 +6,8 @@ import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; import com.strategyobject.substrateclient.pallet.PalletFactory; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; -import com.strategyobject.substrateclient.rpc.api.Index; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import com.strategyobject.substrateclient.rpc.api.section.System; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/BalanceTransfer.java b/api/src/test/java/com/strategyobject/substrateclient/api/BalanceTransfer.java new file mode 100644 index 00000000..6e8b5e62 --- /dev/null +++ b/api/src/test/java/com/strategyobject/substrateclient/api/BalanceTransfer.java @@ -0,0 +1,22 @@ +package com.strategyobject.substrateclient.api; + +import com.strategyobject.substrateclient.rpc.api.AddressId; +import com.strategyobject.substrateclient.rpc.api.Call; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleWriter; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.math.BigInteger; + +@RequiredArgsConstructor +@Getter +@ScaleWriter +public class BalanceTransfer implements Call { + private final byte moduleIndex; + private final byte callIndex; + private final AddressId destination; + @Scale(ScaleType.CompactBigInteger.class) + private final BigInteger amount; +} diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/SystemPallet.java b/api/src/test/java/com/strategyobject/substrateclient/api/SystemPallet.java index 8c53eaa7..b3906592 100644 --- a/api/src/test/java/com/strategyobject/substrateclient/api/SystemPallet.java +++ b/api/src/test/java/com/strategyobject/substrateclient/api/SystemPallet.java @@ -5,8 +5,8 @@ import com.strategyobject.substrateclient.pallet.annotation.StorageHasher; import com.strategyobject.substrateclient.pallet.annotation.StorageKey; import com.strategyobject.substrateclient.pallet.storage.StorageNMap; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.scale.annotation.Scale; @Pallet("System") diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/TestsHelper.java b/api/src/test/java/com/strategyobject/substrateclient/api/TestsHelper.java new file mode 100644 index 00000000..7bc9ce81 --- /dev/null +++ b/api/src/test/java/com/strategyobject/substrateclient/api/TestsHelper.java @@ -0,0 +1,57 @@ +package com.strategyobject.substrateclient.api; + +import com.strategyobject.substrateclient.crypto.ss58.SS58AddressFormat; +import com.strategyobject.substrateclient.rpc.GeneratedRpcSectionFactory; +import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; +import com.strategyobject.substrateclient.rpc.context.RpcEncoderContext; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; +import com.strategyobject.substrateclient.rpc.registries.RpcDecoderRegistry; +import com.strategyobject.substrateclient.rpc.registries.RpcEncoderRegistry; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; +import com.strategyobject.substrateclient.transport.ProviderInterface; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TestsHelper { + public static final MetadataProvider METADATA_PROVIDER = mock(MetadataProvider.class); + + static { + when(METADATA_PROVIDER.getSS58AddressFormat()).thenReturn(SS58AddressFormat.SUBSTRATE_ACCOUNT); + } + + public static final ScaleReaderRegistry SCALE_READER_REGISTRY = new ScaleReaderRegistry() {{ + registerAnnotatedFrom("com.strategyobject.substrateclient"); + }}; + + public static final ScaleWriterRegistry SCALE_WRITER_REGISTRY = new ScaleWriterRegistry() {{ + registerAnnotatedFrom("com.strategyobject.substrateclient"); + }}; + + public static final RpcEncoderRegistry RPC_ENCODER_REGISTRY = new RpcEncoderRegistry(); + + static { + RPC_ENCODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcEncoderContext(METADATA_PROVIDER, RPC_ENCODER_REGISTRY, SCALE_WRITER_REGISTRY), + "com.strategyobject.substrateclient"); + } + + public static final RpcDecoderRegistry RPC_DECODER_REGISTRY = new RpcDecoderRegistry(); + + static { + RPC_DECODER_REGISTRY.registerAnnotatedFrom( + () -> new RpcDecoderContext(METADATA_PROVIDER, RPC_DECODER_REGISTRY, SCALE_READER_REGISTRY), + "com.strategyobject.substrateclient"); + } + + public static GeneratedRpcSectionFactory createSectionFactory(ProviderInterface provider) { + return new GeneratedRpcSectionFactory( + provider, + RPC_ENCODER_REGISTRY, + SCALE_WRITER_REGISTRY, + RPC_DECODER_REGISTRY, + SCALE_READER_REGISTRY); + } +} + diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/pallet/balances/BalancesTest.java b/api/src/test/java/com/strategyobject/substrateclient/api/pallet/balances/BalancesTest.java new file mode 100644 index 00000000..be3e7c81 --- /dev/null +++ b/api/src/test/java/com/strategyobject/substrateclient/api/pallet/balances/BalancesTest.java @@ -0,0 +1,129 @@ +package com.strategyobject.substrateclient.api.pallet.balances; + +import com.strategyobject.substrateclient.api.Api; +import com.strategyobject.substrateclient.api.BalanceTransfer; +import com.strategyobject.substrateclient.api.TestsHelper; +import com.strategyobject.substrateclient.api.pallet.system.EventRecord; +import com.strategyobject.substrateclient.api.pallet.system.System; +import com.strategyobject.substrateclient.common.convert.HexConverter; +import com.strategyobject.substrateclient.common.types.Size; +import com.strategyobject.substrateclient.crypto.Hasher; +import com.strategyobject.substrateclient.crypto.KeyPair; +import com.strategyobject.substrateclient.crypto.KeyRing; +import com.strategyobject.substrateclient.pallet.storage.Arg; +import com.strategyobject.substrateclient.rpc.api.*; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; +import com.strategyobject.substrateclient.rpc.api.section.Author; +import com.strategyobject.substrateclient.rpc.api.section.Chain; +import com.strategyobject.substrateclient.scale.ScaleUtils; +import com.strategyobject.substrateclient.scale.ScaleWriter; +import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; +import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; +import com.strategyobject.substrateclient.transport.ws.WsProvider; +import lombok.val; +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.iterableWithSize; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Testcontainers +class BalancesTest { + private static final int WAIT_TIMEOUT = 30; + + @Container + private final TestSubstrateContainer substrate = new TestSubstrateContainer(SubstrateVersion.V3_0_0); + + @Test + void transfer() throws Exception { + val wsProvider = WsProvider.builder().setEndpoint(substrate.getWsAddress()); + + try (val api = Api.with(wsProvider).build().join()) { + AtomicReference> eventRecords = new AtomicReference<>(new ArrayList<>()); + val unsubscribe = api.pallet(System.class).events() + .subscribe((exception, block, value, keys) -> { + if (exception != null) { + throw new RuntimeException(exception); + } + + eventRecords.set(value); + }, Arg.EMPTY) + .join(); + + doTransfer(api); + + await() + .atMost(WAIT_TIMEOUT, TimeUnit.SECONDS) + .untilAtomic(eventRecords, iterableWithSize(greaterThan(1))); + + assertTrue(unsubscribe.get().join()); + Supplier> events = () -> eventRecords.get().stream().map(x -> x.getEvent().getEvent()); + assertTrue(events.get().anyMatch(x -> x instanceof Balances.Transfer)); + assertTrue(events.get().anyMatch(x -> x instanceof System.ExtrinsicSuccess)); + } + } + + private void doTransfer(Api api) { + val genesis = api.rpc(Chain.class).getBlockHash(BlockNumber.GENESIS).join(); + assertDoesNotThrow(() -> + api.rpc(Author.class).submitExtrinsic(createBalanceTransferExtrinsic(genesis)).join()); + } + + @SuppressWarnings({"unchecked"}) + private Extrinsic createBalanceTransferExtrinsic(BlockHash genesis) { + val specVersion = 264; + val txVersion = 2; + val moduleIndex = (byte) 6; + val callIndex = (byte) 0; + val tip = 0; + val call = new BalanceTransfer(moduleIndex, callIndex, AddressId.fromBytes(bobKeyPair().asPublicKey().getBytes()), BigInteger.valueOf(10)); + + val extra = new SignedExtra<>(specVersion, txVersion, genesis, genesis, new ImmortalEra(), Index.of(0), BigInteger.valueOf(tip)); + val writer = (ScaleWriter>>) TestsHelper.SCALE_WRITER_REGISTRY.resolve(SignedPayload.class); + val signedPayload = ScaleUtils.toBytes(new SignedPayload<>(call, extra), writer); + val keyRing = KeyRing.fromKeyPair(aliceKeyPair()); + + val signature = sign(keyRing, signedPayload); + + return Extrinsic.createSigned( + new SignaturePayload<>( + AddressId.fromBytes(aliceKeyPair().asPublicKey().getBytes()), + signature, + extra + ), call); + } + + private Signature sign(KeyRing keyRing, byte[] payload) { + val signature = payload.length > 256 ? Hasher.blake2(Size.of256, payload) : payload; + + return Sr25519Signature.from(keyRing.sign(() -> signature)); + } + + private KeyPair aliceKeyPair() { + val str = "0x98319d4ff8a9508c4bb0cf0b5a78d760a0b2082c02775e6e82370816fedfff48925a225d97aa00682d6a59b95b18780c10d" + + "7032336e88f3442b42361f4a66011d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"; + + return KeyPair.fromBytes(HexConverter.toBytes(str)); + } + + private KeyPair bobKeyPair() { + val str = "0x081ff694633e255136bdb456c20a5fc8fed21f8b964c11bb17ff534ce80ebd5941ae88f85d0c1bfc37be41c904e1dfc01de" + + "8c8067b0d6d5df25dd1ac0894a3258eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"; + + return KeyPair.fromBytes(HexConverter.toBytes(str)); + } +} \ No newline at end of file diff --git a/api/src/test/java/com/strategyobject/substrateclient/api/pallet/system/SystemTest.java b/api/src/test/java/com/strategyobject/substrateclient/api/pallet/system/SystemTest.java new file mode 100644 index 00000000..f531ac7a --- /dev/null +++ b/api/src/test/java/com/strategyobject/substrateclient/api/pallet/system/SystemTest.java @@ -0,0 +1,77 @@ +package com.strategyobject.substrateclient.api.pallet.system; + +import com.strategyobject.substrateclient.api.Api; +import com.strategyobject.substrateclient.api.pallet.balances.AccountData; +import com.strategyobject.substrateclient.common.convert.HexConverter; +import com.strategyobject.substrateclient.pallet.storage.Arg; +import com.strategyobject.substrateclient.rpc.api.AccountId; +import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; +import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; +import com.strategyobject.substrateclient.transport.ws.WsProvider; +import lombok.val; +import org.junit.jupiter.api.Test; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.math.BigInteger; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.*; + +@Testcontainers +class SystemTest { + private static final int WAIT_TIMEOUT = 30; + + @Container + private final TestSubstrateContainer substrate = new TestSubstrateContainer(SubstrateVersion.V3_0_0); + + @Test + void account() throws Exception { + val wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()); + + try (val api = Api.with(wsProvider).build().join()) { + val system = api.pallet(System.class); + + val alicePublicKey = HexConverter.toBytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"); + val actual = system.account().get(AccountId.fromBytes(alicePublicKey)).join(); + + assertNotNull(actual); + assertEquals( + new BigInteger("1000000000000000000000"), + actual.getData().into(AccountData.class).getFree()); + } + } + + @Test + void events() throws Exception { + val wsProvider = WsProvider.builder() + .setEndpoint(substrate.getWsAddress()); + + try (val api = Api.with(wsProvider).build().join()) { + val system = api.pallet(System.class); + + AtomicReference> eventRecords = new AtomicReference<>(); + val unsubscribe = system.events() + .subscribe((exception, block, value, keys) -> eventRecords.set(value), Arg.EMPTY) + .join(); + + await() + .atMost(WAIT_TIMEOUT, TimeUnit.SECONDS) + .untilAtomic(eventRecords, notNullValue()); + + assertTrue(unsubscribe.get().join()); + assertEquals(1, eventRecords.get().size()); + assertEquals(0, eventRecords.get().get(0).getPhase().getIndex()); + + val eventHolder = eventRecords.get().get(0).getEvent(); + assertEquals(0, eventHolder.getEventIndex()); + assertEquals("System", eventHolder.getPallet().getName()); + assertTrue(eventHolder.getEvent() instanceof System.ExtrinsicSuccess); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/strategyobject/substrateclient/common/reflection/Scanner.java b/common/src/main/java/com/strategyobject/substrateclient/common/reflection/Scanner.java index 75fb3f90..9d68a380 100644 --- a/common/src/main/java/com/strategyobject/substrateclient/common/reflection/Scanner.java +++ b/common/src/main/java/com/strategyobject/substrateclient/common/reflection/Scanner.java @@ -5,6 +5,7 @@ import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Set; @@ -19,15 +20,18 @@ private Scanner(String[] prefixes) { .setUrls( Arrays.stream(prefixes) .flatMap(p -> ClasspathHelper.forPackage(p).stream()) - .collect(Collectors.toCollection(ArrayList::new))) - ); + .collect(Collectors.toCollection(ArrayList::new)))); } - public static Scanner forPrefixes(@NonNull String... prefixes){ + public static Scanner forPrefixes(@NonNull String... prefixes) { return new Scanner(prefixes); } public Set> getSubTypesOf(Class clazz) { return reflections.getSubTypesOf(clazz); } + + public Set> getTypesAnnotatedWith(Class annotation) { + return reflections.getTypesAnnotatedWith(annotation); + } } diff --git a/common/src/main/java/com/strategyobject/substrateclient/common/types/Into.java b/common/src/main/java/com/strategyobject/substrateclient/common/types/Into.java new file mode 100644 index 00000000..af28ed24 --- /dev/null +++ b/common/src/main/java/com/strategyobject/substrateclient/common/types/Into.java @@ -0,0 +1,8 @@ +package com.strategyobject.substrateclient.common.types; + +public interface Into { + @SuppressWarnings("unchecked") + default T into(Class clazz) { + return (T) this; + } +} diff --git a/common/src/main/java/com/strategyobject/substrateclient/common/types/Lambda.java b/common/src/main/java/com/strategyobject/substrateclient/common/types/Lambda.java new file mode 100644 index 00000000..d5099ceb --- /dev/null +++ b/common/src/main/java/com/strategyobject/substrateclient/common/types/Lambda.java @@ -0,0 +1,15 @@ +package com.strategyobject.substrateclient.common.types; + +public final class Lambda { + + public static void noop(T ignored) { + // do nothing + } + + public static void noop(T0 _x, T1 _y) { + // do nothing + } + + private Lambda() { + } +} diff --git a/crypto/build.gradle b/crypto/build.gradle index b837674f..d37a6051 100644 --- a/crypto/build.gradle +++ b/crypto/build.gradle @@ -3,7 +3,7 @@ dependencies { implementation 'com.github.multiformats:java-multibase:1.1.0' implementation 'org.bouncycastle:bcprov-jdk15on:1.70' - implementation 'net.openhft:zero-allocation-hashing:0.15' + implementation 'net.openhft:zero-allocation-hashing:0.16' } test { diff --git a/pallet/build.gradle b/pallet/build.gradle index d4a6d4ae..250fdd93 100644 --- a/pallet/build.gradle +++ b/pallet/build.gradle @@ -13,4 +13,6 @@ dependencies { testImplementation 'org.testcontainers:testcontainers:1.17.2' testImplementation 'org.testcontainers:junit-jupiter:1.17.2' testImplementation 'ch.qos.logback:logback-classic:1.2.11' + testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.hamcrest:hamcrest:2.2' } \ No newline at end of file diff --git a/pallet/pallet-codegen/src/main/java/com/strategyobject/substrateclient/pallet/codegen/PalletAnnotatedInterface.java b/pallet/pallet-codegen/src/main/java/com/strategyobject/substrateclient/pallet/codegen/PalletAnnotatedInterface.java index bbb10371..5799a515 100644 --- a/pallet/pallet-codegen/src/main/java/com/strategyobject/substrateclient/pallet/codegen/PalletAnnotatedInterface.java +++ b/pallet/pallet-codegen/src/main/java/com/strategyobject/substrateclient/pallet/codegen/PalletAnnotatedInterface.java @@ -64,6 +64,10 @@ public void generateClass(ProcessorContext context) throws ProcessingException, val constructorBuilder = createConstructorBuilder(); for (Element method : interfaceElement.getEnclosedElements()) { + if (!(method instanceof ExecutableElement)) { + continue; + } + this.methodProcessor.process(name, (ExecutableElement) method, typeSpecBuilder, diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/annotation/Event.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/annotation/Event.java index 0ede9e77..83862d04 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/annotation/Event.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/annotation/Event.java @@ -14,5 +14,12 @@ /** * @return the index of an event */ - int value(); + int index(); + + /** + * Provide a pallet name that corresponds to the event if event class is not defined inside a pallet interface. + * + * @return pallet name that corresponds to the event + */ + String pallet() default ""; } diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptor.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptor.java new file mode 100644 index 00000000..f82c9c97 --- /dev/null +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptor.java @@ -0,0 +1,28 @@ +package com.strategyobject.substrateclient.pallet.events; + +import com.strategyobject.substrateclient.rpc.metadata.Pallet; +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * The event descriptor holding event description data and the event itself. + */ +@Getter +@RequiredArgsConstructor +public class EventDescriptor { + /** + * Reference to the Pallet that produced the event. + */ + private final Pallet pallet; + + /** + * Index of the event. + */ + private final int eventIndex; + + /** + * The event itself. + */ + private final @NonNull Object event; +} diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptorReader.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptorReader.java new file mode 100644 index 00000000..694dd997 --- /dev/null +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventDescriptorReader.java @@ -0,0 +1,39 @@ +package com.strategyobject.substrateclient.pallet.events; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.common.io.Streamer; +import com.strategyobject.substrateclient.rpc.metadata.MetadataProvider; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +import java.io.IOException; +import java.io.InputStream; + +@RequiredArgsConstructor +public class EventDescriptorReader implements ScaleReader { + private final @NonNull ScaleReaderRegistry scaleReaderRegistry; + private final @NonNull MetadataProvider metadataProvider; + private final @NonNull EventRegistry eventRegistry; + + @Override + public EventDescriptor read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { + Preconditions.checkArgument(readers == null || readers.length == 0); + + val palletIndex = Streamer.readByte(stream); + val pallet = metadataProvider.getPallets().get(palletIndex); + val eventIndex = Streamer.readByte(stream); + + val eventClass = eventRegistry.resolve(pallet.getName(), eventIndex); + if (eventClass == null) { + throw new RuntimeException( + String.format("Unknown event with index %d in pallet %s", eventIndex, pallet.getName())); + } + + val eventReader = scaleReaderRegistry.resolve(eventClass); + val event = eventReader.read(stream); + return new EventDescriptor(pallet, eventIndex, event); + } +} diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventRegistry.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventRegistry.java new file mode 100644 index 00000000..7690359c --- /dev/null +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/events/EventRegistry.java @@ -0,0 +1,78 @@ +package com.strategyobject.substrateclient.pallet.events; + +import com.google.common.base.Strings; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.strategyobject.substrateclient.common.reflection.Scanner; +import com.strategyobject.substrateclient.pallet.annotation.Event; +import com.strategyobject.substrateclient.pallet.annotation.Pallet; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import lombok.var; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +@Slf4j +public class EventRegistry { + private final Map>> map = new ConcurrentHashMap<>(32); + + private Collection> expand(Class event, int offsetSize) { + return Lists.newArrayList( + Iterables.concat( + Collections.nCopies(offsetSize, null), + Collections.singleton(event))); + } + + public void registerAnnotatedFrom(String... prefixes) { + + Scanner.forPrefixes(prefixes) + .getTypesAnnotatedWith(Event.class) + .stream() + .sorted(Comparator.>comparingInt(x -> x.getAnnotation(Event.class).index()).reversed()) + .forEach(event -> { + val annotation = event.getAnnotation(Event.class); + + try { + val eventIndex = annotation.index(); + var palletName = annotation.pallet(); + if (Strings.isNullOrEmpty(palletName)) { + val pallet = event.getDeclaringClass(); + val palletAnnotation = pallet.getAnnotation(Pallet.class); + palletName = palletAnnotation.value(); + } + + log.info("Auto register event {} with index {} from {}", + event.getSimpleName(), + eventIndex, + palletName); + + register(palletName, eventIndex, event); + } catch (Exception e) { + log.error("Auto registration error", e); + } + }); + } + + public void register(@NonNull String pallet, int eventIndex, @NonNull Class event) { + val events = map.get(pallet); + if (events == null) { + map.put(pallet, new CopyOnWriteArrayList<>(expand(event, eventIndex))); + } else if (events.size() <= eventIndex) { + events.addAll(expand(event, eventIndex - events.size())); + } else { + events.set(eventIndex, event); + } + } + + public Class resolve(@NonNull String pallet, int eventIndex) { + val events = map.get(pallet); + if (events == null) { + return null; + } + + return events.get(eventIndex); + } +} diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/Arg.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/Arg.java index 7f64566e..17101978 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/Arg.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/Arg.java @@ -8,6 +8,8 @@ * It contains the list of keys which are required for the specific storage. */ public class Arg { + public static final Arg EMPTY = new Arg(new Object[0]); + /** * List of keys. */ @@ -20,6 +22,7 @@ private Arg(Object[] list) { /** * Creates a new Arg. + * * @param keys any number of keys. * @return a new Arg with given keys. */ diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/DiverseKeyValueCollection.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/DiverseKeyValueCollection.java index 5b49e527..61966be2 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/DiverseKeyValueCollection.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/DiverseKeyValueCollection.java @@ -2,8 +2,8 @@ import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.StorageData; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageData; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; import lombok.val; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/HomogeneousKeyValueCollection.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/HomogeneousKeyValueCollection.java index dfaaa402..37868cdb 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/HomogeneousKeyValueCollection.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/HomogeneousKeyValueCollection.java @@ -1,8 +1,8 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.StorageData; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageData; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; import lombok.RequiredArgsConstructor; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/KeyCollectionImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/KeyCollectionImpl.java index 46c65c21..ca0864f2 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/KeyCollectionImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/KeyCollectionImpl.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.rpc.api.section.State; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; import lombok.RequiredArgsConstructor; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKey.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKey.java index e9c61618..b78b15ea 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKey.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKey.java @@ -1,6 +1,6 @@ package com.strategyobject.substrateclient.pallet.storage; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKeyImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKeyImpl.java index 9fa02690..2dba3670 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKeyImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/QueryableKeyImpl.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.rpc.api.section.State; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; import lombok.RequiredArgsConstructor; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageChangeConsumer.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageChangeConsumer.java index 3a013118..2ccac39d 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageChangeConsumer.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageChangeConsumer.java @@ -1,6 +1,6 @@ package com.strategyobject.substrateclient.pallet.storage; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import java.util.List; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMap.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMap.java index 377e026a..dd413d35 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMap.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMap.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.pallet.storage; import com.google.common.annotations.Beta; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import lombok.NonNull; import java.util.concurrent.CompletableFuture; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMapImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMapImpl.java index 33e49f47..11e111a5 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMapImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageDoubleMapImpl.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.pallet.storage; import com.google.common.annotations.Beta; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProvider.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProvider.java index 8cf28896..e3aef253 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProvider.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProvider.java @@ -2,7 +2,7 @@ import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.crypto.Hasher; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import lombok.*; import java.io.ByteArrayInputStream; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMap.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMap.java index 305055cc..508c6deb 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMap.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMap.java @@ -2,7 +2,7 @@ import com.google.common.annotations.Beta; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import lombok.NonNull; import java.util.List; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImpl.java index 69b5989f..feb38751 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImpl.java @@ -2,7 +2,7 @@ import com.google.common.annotations.Beta; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMap.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMap.java index 2726d971..7fa1e80b 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMap.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMap.java @@ -1,8 +1,8 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.Hash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; import lombok.NonNull; import java.util.List; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImpl.java index a7553ebc..32796cc1 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImpl.java @@ -1,10 +1,10 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.Hash; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; import com.strategyobject.substrateclient.rpc.api.section.State; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -310,7 +310,8 @@ public CompletableFuture> multi(@NonNull Arg... args) { } @Override - public CompletableFuture>> subscribe(@NonNull StorageChangeConsumer consumer, @NonNull Arg... args) { + public CompletableFuture>> subscribe(@NonNull StorageChangeConsumer consumer, + @NonNull Arg... args) { if (args.length == 0) { throw new IllegalArgumentException("Subscription can't be requested with no arguments."); } diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValue.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValue.java index 54df340a..699f59c0 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValue.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValue.java @@ -2,7 +2,7 @@ import com.google.common.annotations.Beta; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import lombok.NonNull; import java.util.List; diff --git a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImpl.java b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImpl.java index 0e0d6703..d6c121e1 100644 --- a/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImpl.java +++ b/pallet/src/main/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImpl.java @@ -2,7 +2,7 @@ import com.google.common.annotations.Beta; import com.strategyobject.substrateclient.common.types.tuple.Pair; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.scale.ScaleReader; import lombok.NonNull; diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/IdentityTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/IdentityTests.java index efcc9991..d11424e2 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/IdentityTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/IdentityTests.java @@ -1,8 +1,8 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.pallet.TestsHelper; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.impl.Hash256; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash256; import com.strategyobject.substrateclient.scale.ScaleWriter; import lombok.SneakyThrows; import lombok.val; diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/KeyHasherTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/KeyHasherTests.java index 9180264e..3a0e58d6 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/KeyHasherTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/KeyHasherTests.java @@ -4,8 +4,8 @@ import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import com.strategyobject.substrateclient.pallet.TestsHelper; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.impl.Hash256; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash256; import com.strategyobject.substrateclient.scale.ScaleReader; import com.strategyobject.substrateclient.scale.ScaleWriter; import com.strategyobject.substrateclient.scale.readers.CompactIntegerReader; diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProviderTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProviderTests.java index 2dc66e4a..f9469b48 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProviderTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageKeyProviderTests.java @@ -4,7 +4,7 @@ import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import com.strategyobject.substrateclient.pallet.TestsHelper; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.ScaleReader; import com.strategyobject.substrateclient.scale.ScaleWriter; import lombok.NonNull; diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImplTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImplTests.java index 161e6083..71baa5af 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImplTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageMapImplTests.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.pallet.storage; import com.strategyobject.substrateclient.pallet.TestsHelper; -import com.strategyobject.substrateclient.rpc.api.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.scale.ScaleReader; import com.strategyobject.substrateclient.scale.ScaleWriter; diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImplTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImplTests.java index 3b429095..d4090ad8 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImplTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageNMapImplTests.java @@ -4,8 +4,8 @@ import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import com.strategyobject.substrateclient.pallet.TestsHelper; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.rpc.api.section.Chain; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.scale.ScaleReader; @@ -31,9 +31,9 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.greaterThan; import static org.junit.jupiter.api.Assertions.*; -import static org.testcontainers.shaded.org.awaitility.Awaitility.await; -import static org.testcontainers.shaded.org.hamcrest.number.OrderingComparison.greaterThan; @Testcontainers class StorageNMapImplTests { diff --git a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImplTests.java b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImplTests.java index da4e3287..ca5f43db 100644 --- a/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImplTests.java +++ b/pallet/src/test/java/com/strategyobject/substrateclient/pallet/storage/StorageValueImplTests.java @@ -3,7 +3,7 @@ import com.strategyobject.substrateclient.crypto.ss58.SS58Codec; import com.strategyobject.substrateclient.pallet.TestsHelper; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.rpc.api.section.Chain; import com.strategyobject.substrateclient.rpc.api.section.State; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; diff --git a/readme.md b/readme.md index 3f7afc25..91794749 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ - + # About @@ -40,7 +40,7 @@ The best approach to reach project’s goals is to use annotations and code gene - [x] `@Pallet`; - [ ] `@Transaction`; - [x] `@Storage`; - - [ ] `@EventHandler`. + - [x] `@Event`. These allow the generation of scale serializers, deserializers, RPC methods, code for interaction with pallet, etc. More examples you can find below. @@ -70,4 +70,4 @@ We take care of either lost responses or canceled futures by not holding handler - ### Tests run with substrate node. All API methods related to the substrate node will be tested for operability and compatibility. -Currently we use [test containers](https://www.testcontainers.org/) and docker image [parity/substrate:v3.0.0](https://hub.docker.com/layers/parity/substrate/v3.0.0/images/sha256-1aef07509d757c584320773c476dcb6077578bbf2f5e468ceb413dcf908897f1?context=explore). +Currently, we use [test containers](https://www.testcontainers.org/) and docker image [parity/substrate:v3.0.0](https://hub.docker.com/layers/parity/substrate/v3.0.0/images/sha256-1aef07509d757c584320773c476dcb6077578bbf2f5e468ceb413dcf908897f1?context=explore). diff --git a/rpc/rpc-api/build.gradle b/rpc/rpc-api/build.gradle index e7b52375..c1d23329 100644 --- a/rpc/rpc-api/build.gradle +++ b/rpc/rpc-api/build.gradle @@ -16,4 +16,5 @@ dependencies { testImplementation 'org.testcontainers:testcontainers:1.17.2' testImplementation 'org.testcontainers:junit-jupiter:1.17.2' testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.hamcrest:hamcrest:2.2' } \ No newline at end of file diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BalanceStatus.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BalanceStatus.java new file mode 100644 index 00000000..1e8dfb15 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BalanceStatus.java @@ -0,0 +1,19 @@ +package com.strategyobject.substrateclient.rpc.api; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * Status of funds. + */ +@ScaleReader +public enum BalanceStatus { + /** + * Funds are free, as corresponding to `free` item in Balances. + */ + FREE, + + /** + * Funds are reserved, as corresponding to `reserved` item in Balances. + */ + RESERVED +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockHash.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockHash.java deleted file mode 100644 index 60e5568b..00000000 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockHash.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.strategyobject.substrateclient.rpc.api; - -public interface BlockHash extends Hash { -} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BytesEncoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BytesEncoder.java index 47a4f091..7d816e74 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BytesEncoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BytesEncoder.java @@ -6,7 +6,9 @@ import com.strategyobject.substrateclient.rpc.EncoderPair; import com.strategyobject.substrateclient.rpc.RpcEncoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; -import com.strategyobject.substrateclient.rpc.api.impl.Hash256; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash256; @AutoRegister( types = { diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Event.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Event.java deleted file mode 100644 index 965d0f97..00000000 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Event.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.strategyobject.substrateclient.rpc.api; - -public interface Event { -} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatus.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatus.java index a607c5a0..78fd0e70 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatus.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/ExtrinsicStatus.java @@ -1,6 +1,7 @@ package com.strategyobject.substrateclient.rpc.api; import com.strategyobject.substrateclient.rpc.annotation.RpcDecoder; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.scale.annotation.Scale; import lombok.Getter; import lombok.NoArgsConstructor; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/FixedBytesWriter.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/FixedBytesWriter.java index 5bd02b88..299b8030 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/FixedBytesWriter.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/FixedBytesWriter.java @@ -6,7 +6,9 @@ import com.strategyobject.substrateclient.common.types.Size; import com.strategyobject.substrateclient.crypto.PublicKey; import com.strategyobject.substrateclient.crypto.SignatureData; -import com.strategyobject.substrateclient.rpc.api.impl.Hash256; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash256; import com.strategyobject.substrateclient.scale.ScaleWriter; import com.strategyobject.substrateclient.scale.annotation.AutoRegister; import lombok.NonNull; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Header.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Header.java index 5ffe7dff..635e5664 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Header.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Header.java @@ -1,6 +1,8 @@ package com.strategyobject.substrateclient.rpc.api; import com.strategyobject.substrateclient.rpc.annotation.RpcDecoder; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.scale.annotation.Scale; import lombok.Getter; import lombok.Setter; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedAdditionalExtra.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedAdditionalExtra.java index 22675a7b..9048ccc8 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedAdditionalExtra.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedAdditionalExtra.java @@ -1,5 +1,6 @@ package com.strategyobject.substrateclient.rpc.api; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.scale.ScaleType; import com.strategyobject.substrateclient.scale.annotation.Scale; import com.strategyobject.substrateclient.scale.annotation.ScaleWriter; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtra.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtra.java index 634c4d5a..4b77207d 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtra.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtra.java @@ -1,5 +1,7 @@ package com.strategyobject.substrateclient.rpc.api; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import com.strategyobject.substrateclient.scale.ScaleType; import com.strategyobject.substrateclient.scale.annotation.Ignore; import com.strategyobject.substrateclient.scale.annotation.Scale; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtraWriter.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtraWriter.java index b2b44a26..33972480 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtraWriter.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/SignedExtraWriter.java @@ -1,6 +1,7 @@ package com.strategyobject.substrateclient.rpc.api; import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import com.strategyobject.substrateclient.scale.ScaleDispatch; import com.strategyobject.substrateclient.scale.ScaleType; import com.strategyobject.substrateclient.scale.ScaleWriter; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndex.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndex.java new file mode 100644 index 00000000..7a40ed5b --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndex.java @@ -0,0 +1,23 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.math.BigInteger; + +/** + * Index of an account. + */ +@RequiredArgsConstructor(staticName = "of") +@Getter +@EqualsAndHashCode +public class AccountIndex { + public static final AccountIndex ZERO = AccountIndex.of(BigInteger.ZERO); + + private final BigInteger value; + + public static AccountIndex of(long value) { + return of(BigInteger.valueOf(value)); + } +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndexU32Reader.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndexU32Reader.java new file mode 100644 index 00000000..fd1ed428 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/AccountIndexU32Reader.java @@ -0,0 +1,28 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +import java.io.IOException; +import java.io.InputStream; + +@AutoRegister(types = AccountIndex.class) +@RequiredArgsConstructor +public class AccountIndexU32Reader implements ScaleReader { + private final @NonNull ScaleReaderRegistry registry; + + @SuppressWarnings("unchecked") + @Override + public AccountIndex read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { + Preconditions.checkArgument(readers == null || readers.length == 0); + + val u32Reader = (ScaleReader) registry.resolve(ScaleType.U32.class); + return AccountIndex.of(u32Reader.read(stream)); + } +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Balance.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Balance.java new file mode 100644 index 00000000..7a5790f4 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Balance.java @@ -0,0 +1,19 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.math.BigInteger; + +/** + * Balance of an account. + */ +@RequiredArgsConstructor(staticName = "of") +@Getter +@EqualsAndHashCode +public class Balance { + public static final Balance ZERO = Balance.of(BigInteger.ZERO); + + private final BigInteger value; +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BalanceU128Reader.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BalanceU128Reader.java new file mode 100644 index 00000000..65ba32e8 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BalanceU128Reader.java @@ -0,0 +1,29 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; + +@AutoRegister(types = Balance.class) +@RequiredArgsConstructor +public class BalanceU128Reader implements ScaleReader { + private final @NonNull ScaleReaderRegistry registry; + + @SuppressWarnings("unchecked") + @Override + public Balance read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { + Preconditions.checkArgument(readers == null || readers.length == 0); + + val u128Reader = (ScaleReader) registry.resolve(ScaleType.U128.class); + return Balance.of(u128Reader.read(stream)); + } +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockHash.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockHash.java new file mode 100644 index 00000000..798df230 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockHash.java @@ -0,0 +1,4 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +public interface BlockHash extends Hash { +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumber.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumber.java similarity index 82% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumber.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumber.java index c70526e2..1b276a82 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumber.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumber.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import lombok.EqualsAndHashCode; import lombok.Getter; @@ -6,6 +6,9 @@ import java.math.BigInteger; +/** + * An index of a block. + */ @RequiredArgsConstructor(staticName = "of") @Getter @EqualsAndHashCode diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberDecoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberDecoder.java similarity index 91% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberDecoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberDecoder.java index 2a69cc92..8595e1a3 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberDecoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberDecoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberEncoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberEncoder.java similarity index 87% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberEncoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberEncoder.java index 622f65cf..623456db 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberEncoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberEncoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.rpc.EncoderPair; import com.strategyobject.substrateclient.rpc.RpcEncoder; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Reader.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Reader.java similarity index 94% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Reader.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Reader.java index 9b69af1d..8ca213d2 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Reader.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Reader.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.scale.ScaleReader; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Writer.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Writer.java similarity index 94% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Writer.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Writer.java index 01a63156..18818788 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/BlockNumberU32Writer.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/BlockNumberU32Writer.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.scale.ScaleType; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/CallHash.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/CallHash.java new file mode 100644 index 00000000..af6477e8 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/CallHash.java @@ -0,0 +1,4 @@ +package com.strategyobject.substrateclient.rpc.api.primitives; + +public interface CallHash extends Hash { +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Hash.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash.java similarity index 58% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Hash.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash.java index e9c8ed0b..b067ff23 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Hash.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash.java @@ -1,8 +1,11 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.common.types.Bytes; import lombok.NonNull; +/** + * A hash of some data used by the chain. + */ public interface Hash extends Bytes { byte @NonNull [] getBytes(); } diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256.java similarity index 67% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256.java index 89429518..ffbe0857 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256.java @@ -1,12 +1,10 @@ -package com.strategyobject.substrateclient.rpc.api.impl; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.common.types.FixedBytes; import com.strategyobject.substrateclient.common.types.Size; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.Hash; import lombok.NonNull; -public class Hash256 extends FixedBytes implements Hash, BlockHash { +public class Hash256 extends FixedBytes implements Hash, BlockHash, CallHash { private Hash256(byte[] data) { super(data, Size.of32); } diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Decoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Decoder.java similarity index 78% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Decoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Decoder.java index 73372f5c..d1f0e6cb 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Decoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Decoder.java @@ -1,14 +1,12 @@ -package com.strategyobject.substrateclient.rpc.api.impl; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.common.convert.HexConverter; import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.Hash; import com.strategyobject.substrateclient.rpc.decoders.AbstractDecoder; import com.strategyobject.substrateclient.transport.RpcObject; -@AutoRegister(types = {Hash256.class, Hash.class, BlockHash.class}) +@AutoRegister(types = {Hash256.class, Hash.class, BlockHash.class, CallHash.class}) public class Hash256Decoder extends AbstractDecoder { @Override protected Hash256 decodeNonNull(RpcObject value, DecoderPair[] decoders) { diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Reader.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Reader.java similarity index 81% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Reader.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Reader.java index 67c6a08e..7e4d6ad1 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/impl/Hash256Reader.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Hash256Reader.java @@ -1,10 +1,8 @@ -package com.strategyobject.substrateclient.rpc.api.impl; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.common.io.Streamer; import com.strategyobject.substrateclient.common.types.Size; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.Hash; import com.strategyobject.substrateclient.scale.ScaleReader; import com.strategyobject.substrateclient.scale.annotation.AutoRegister; import lombok.NonNull; @@ -12,7 +10,7 @@ import java.io.IOException; import java.io.InputStream; -@AutoRegister(types = {Hash256.class, Hash.class, BlockHash.class}) +@AutoRegister(types = {Hash256.class, Hash.class, BlockHash.class, CallHash.class}) public class Hash256Reader implements ScaleReader { @Override public Hash256 read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Index.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Index.java similarity index 85% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Index.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Index.java index 2c48c742..ef7c1639 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/Index.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/Index.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.scale.ScaleType; import com.strategyobject.substrateclient.scale.annotation.Scale; @@ -9,6 +9,9 @@ import java.math.BigInteger; +/** + * Index of a transaction in the chain. + */ @RequiredArgsConstructor(staticName = "of") @Getter @EqualsAndHashCode diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/IndexDecoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/IndexDecoder.java similarity index 89% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/IndexDecoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/IndexDecoder.java index 112f879f..f431fa76 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/IndexDecoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/primitives/IndexDecoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.primitives; import com.strategyobject.substrateclient.rpc.DecoderPair; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ArithmeticError.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ArithmeticError.java new file mode 100644 index 00000000..3390f051 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ArithmeticError.java @@ -0,0 +1,24 @@ +package com.strategyobject.substrateclient.rpc.api.runtime; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * Arithmetic errors. + */ +@ScaleReader +public enum ArithmeticError { + /** + * / Underflow. + */ + UNDERFLOW, + + /** + * / Overflow. + */ + OVERFLOW, + + /** + * / Division by zero. + */ + DIVISION_BY_ZERO +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchError.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchError.java new file mode 100644 index 00000000..d79cf424 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchError.java @@ -0,0 +1,144 @@ +package com.strategyobject.substrateclient.rpc.api.runtime; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.common.types.union.Union; + +/** + * Reason why a dispatch call failed. + */ +public class DispatchError extends Union { + + private DispatchError() { + } + + /** + * @return true if some error occurred + */ + public boolean isOther() { + return index == 0; + } + + /** + * @return true if failed to lookup some data + */ + public boolean isCannotLookup() { + return index == 1; + } + + /** + * @return true if is a bad origin + */ + public boolean isBadOrigin() { + return index == 2; + } + + /** + * @return true if is a custom error in a module + */ + public boolean isModule() { + return index == 3; + } + + /** + * @return true if at least one consumer is remaining so the account cannot be destroyed + */ + public boolean isConsumerRemaining() { + return index == 4; + } + + /** + * @return true if there are no providers so the account cannot be created + */ + public boolean isNoProviders() { + return index == 5; + } + + /** + * @return true if is an error to do with tokens + */ + public boolean isToken() { + return index == 6; + } + + /** + * @return true if is an arithmetic error + */ + public boolean isArithmetic() { + return index == 7; + } + + /** + * @return module error + */ + public ModuleError getModuleError() { + Preconditions.checkState(index == 3); + return (ModuleError) value; + } + + /** + * @return token error + */ + public TokenError getTokenError() { + Preconditions.checkState(index == 6); + return (TokenError) value; + } + + /** + * @return arithmetic error + */ + public ArithmeticError getArithmeticError() { + Preconditions.checkState(index == 7); + return (ArithmeticError) value; + } + + public static DispatchError ofOther() { + DispatchError result = new DispatchError(); + result.index = 0; + return result; + } + + public static DispatchError ofCannotLookup() { + DispatchError result = new DispatchError(); + result.index = 1; + return result; + } + + public static DispatchError ofBadOrigin() { + DispatchError result = new DispatchError(); + result.index = 2; + return result; + } + + public static DispatchError ofModule(ModuleError moduleError) { + DispatchError result = new DispatchError(); + result.value = moduleError; + result.index = 3; + return result; + } + + public static DispatchError ofConsumerRemaining() { + DispatchError result = new DispatchError(); + result.index = 4; + return result; + } + + public static DispatchError ofNoProviders() { + DispatchError result = new DispatchError(); + result.index = 5; + return result; + } + + public static DispatchError ofToken(TokenError tokenError) { + DispatchError result = new DispatchError(); + result.value = tokenError; + result.index = 6; + return result; + } + + public static DispatchError ofArithmetic(ArithmeticError arithmeticError) { + DispatchError result = new DispatchError(); + result.value = arithmeticError; + result.index = 7; + return result; + } +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchErrorReader.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchErrorReader.java new file mode 100644 index 00000000..21378c8a --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/DispatchErrorReader.java @@ -0,0 +1,41 @@ +package com.strategyobject.substrateclient.rpc.api.runtime; + +import com.google.common.base.Preconditions; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.readers.union.BaseUnionReader; +import com.strategyobject.substrateclient.scale.registries.ScaleReaderRegistry; +import lombok.NonNull; +import lombok.val; + +import java.io.IOException; +import java.io.InputStream; + +@AutoRegister(types = DispatchError.class) +public class DispatchErrorReader extends BaseUnionReader { + private final ScaleReaderRegistry registry; + + public DispatchErrorReader(ScaleReaderRegistry registry) { + super(8, + x -> DispatchError.ofOther(), + x -> DispatchError.ofCannotLookup(), + x -> DispatchError.ofBadOrigin(), + x -> DispatchError.ofModule((ModuleError) x), + x -> DispatchError.ofConsumerRemaining(), + x -> DispatchError.ofNoProviders(), + x -> DispatchError.ofToken((TokenError) x), + x -> DispatchError.ofArithmetic((ArithmeticError) x)); + + this.registry = registry; + } + + @Override + public DispatchError read(@NonNull InputStream stream, ScaleReader... readers) throws IOException { + Preconditions.checkArgument(readers == null || readers.length == 0); + + val moduleErrorReader = registry.resolve(ModuleError.class); + val tokenErrorReader = registry.resolve(TokenError.class); + val arithmeticErrorReader = registry.resolve(ArithmeticError.class); + return super.read(stream, moduleErrorReader, tokenErrorReader, arithmeticErrorReader); + } +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ModuleError.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ModuleError.java new file mode 100644 index 00000000..d9971981 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/ModuleError.java @@ -0,0 +1,27 @@ +package com.strategyobject.substrateclient.rpc.api.runtime; + +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +/** + * A custom error in a module. + */ +@ScaleReader +@Getter +@Setter +public class ModuleError { + /** + * Module index, matching the metadata module index. + */ + @Scale(ScaleType.U8.class) + private Integer index; + + /** + * Module specific error value. + */ + @Scale(ScaleType.U8.class) + private Integer error; +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/TokenError.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/TokenError.java new file mode 100644 index 00000000..6fa3dc8c --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/runtime/TokenError.java @@ -0,0 +1,44 @@ +package com.strategyobject.substrateclient.rpc.api.runtime; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * Description of what went wrong when trying to complete an operation on a token. + */ +@ScaleReader +public enum TokenError { + /** + * / Funds are unavailable. + */ + NO_FUNDS, + + /** + * / Account that must exist would die. + */ + WOULD_DIE, + + /** + * / Account cannot exist with the funds that would be given. + */ + BELOW_MINIMUM, + + /** + * / Account cannot be created. + */ + CANNOT_CREATE, + + /** + * / The asset in question is unknown. + */ + UNKNOWN_ASSET, + + /** + * / Funds exist but are frozen. + */ + FROZEN, + + /** + * / Operation is not supported by the asset. + */ + UNSUPPORTED, +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Author.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Author.java index b0bcd48d..cce2d675 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Author.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Author.java @@ -6,7 +6,7 @@ import com.strategyobject.substrateclient.rpc.annotation.RpcSubscription; import com.strategyobject.substrateclient.rpc.api.Extrinsic; import com.strategyobject.substrateclient.rpc.api.ExtrinsicStatus; -import com.strategyobject.substrateclient.rpc.api.Hash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; import com.strategyobject.substrateclient.scale.annotation.Scale; import com.strategyobject.substrateclient.scale.annotation.ScaleGeneric; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Chain.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Chain.java index 7b6564a2..c5b52aae 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Chain.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/Chain.java @@ -3,8 +3,8 @@ import com.strategyobject.substrateclient.rpc.annotation.RpcCall; import com.strategyobject.substrateclient.rpc.annotation.RpcInterface; import com.strategyobject.substrateclient.rpc.annotation.RpcSubscription; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.rpc.api.Header; import com.strategyobject.substrateclient.rpc.api.SignedBlock; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/State.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/State.java index 1df70a31..2251dcec 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/State.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/State.java @@ -4,6 +4,11 @@ import com.strategyobject.substrateclient.rpc.annotation.RpcInterface; import com.strategyobject.substrateclient.rpc.annotation.RpcSubscription; import com.strategyobject.substrateclient.rpc.api.*; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.Hash; +import com.strategyobject.substrateclient.rpc.api.storage.StorageChangeSet; +import com.strategyobject.substrateclient.rpc.api.storage.StorageData; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.scale.annotation.Scale; import java.util.List; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/System.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/System.java index 9c929397..5db276bc 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/System.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/section/System.java @@ -3,7 +3,7 @@ import com.strategyobject.substrateclient.rpc.annotation.RpcCall; import com.strategyobject.substrateclient.rpc.annotation.RpcInterface; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.Index; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import java.util.concurrent.CompletableFuture; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageChangeSet.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageChangeSet.java similarity index 76% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageChangeSet.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageChangeSet.java index 708f6da6..9aa2b3b7 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageChangeSet.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageChangeSet.java @@ -1,7 +1,8 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import com.strategyobject.substrateclient.common.types.tuple.Pair; import com.strategyobject.substrateclient.rpc.annotation.RpcDecoder; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; import com.strategyobject.substrateclient.scale.annotation.Scale; import lombok.Getter; import lombok.Setter; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageData.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageData.java similarity index 75% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageData.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageData.java index ae945514..f10f43d9 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageData.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageData.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageDataDecoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageDataDecoder.java similarity index 91% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageDataDecoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageDataDecoder.java index 15c0c414..88e28a40 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageDataDecoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageDataDecoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import com.strategyobject.substrateclient.common.convert.HexConverter; import com.strategyobject.substrateclient.rpc.DecoderPair; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKey.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKey.java similarity index 74% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKey.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKey.java index 8c81e398..74bfbc55 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKey.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKey.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import lombok.Getter; import lombok.RequiredArgsConstructor; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyDecoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyDecoder.java similarity index 91% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyDecoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyDecoder.java index aae8f4b0..a514e54d 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyDecoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyDecoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import com.strategyobject.substrateclient.common.convert.HexConverter; import com.strategyobject.substrateclient.rpc.DecoderPair; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyEncoder.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyEncoder.java similarity index 91% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyEncoder.java rename to rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyEncoder.java index ae05cf67..0cd75390 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/StorageKeyEncoder.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/storage/StorageKeyEncoder.java @@ -1,4 +1,4 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.api.storage; import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.common.convert.HexConverter; diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchClass.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchClass.java new file mode 100644 index 00000000..7e60c13a --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchClass.java @@ -0,0 +1,33 @@ +package com.strategyobject.substrateclient.rpc.api.weights; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * A generalized group of dispatch types. + */ +@ScaleReader +public enum DispatchClass { + /** + * A normal dispatch. + */ + NORMAL, + + /** + * An operational dispatch. + */ + OPERATIONAL, + + /** + * A mandatory dispatch. These kinds of dispatch are always included regardless of their + * weight, therefore it is critical that they are separately validated to ensure that a + * malicious validator cannot craft a valid but impossibly heavy block. Usually this just means + * ensuring that the extrinsic can only be included once and that it is always very light. + *

+ * The only real use case for this is inherent extrinsics that are required to execute in a + * block for the block to be valid, and it solves the issue in the case that the block + * initialization is sufficiently heavy to mean that those inherents do not fit into the + * block. Essentially, we assume that in these exceptional circumstances, it is better to + * allow an overweight block to be created than to not allow any block at all to be created. + */ + MANDATORY +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchInfo.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchInfo.java new file mode 100644 index 00000000..b6f94f5b --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/DispatchInfo.java @@ -0,0 +1,33 @@ +package com.strategyobject.substrateclient.rpc.api.weights; + +import com.strategyobject.substrateclient.scale.ScaleType; +import com.strategyobject.substrateclient.scale.annotation.Scale; +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import lombok.Getter; +import lombok.Setter; + +import java.math.BigInteger; + +/** + * A bundle of static information collected from the `weight` attributes. + */ +@Getter +@Setter +@ScaleReader +public class DispatchInfo { + /** + * Weight of this transaction. + */ + @Scale(ScaleType.U64.class) + private BigInteger weight; + + /** + * Class of this transaction. + */ + private DispatchClass dispatchClass; + + /** + * Does this transaction pay fees. + */ + private Pays paysFee; +} diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/Pays.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/Pays.java new file mode 100644 index 00000000..dcf5ffd8 --- /dev/null +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/weights/Pays.java @@ -0,0 +1,19 @@ +package com.strategyobject.substrateclient.rpc.api.weights; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; + +/** + * Explicit enum to denote if a transaction pays fee or not. + */ +@ScaleReader +public enum Pays { + /** + * Transactor will pay related fees. + */ + YES, + + /** + * Transactor will NOT pay related fees. + */ + NO +} diff --git a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/AuthorTests.java b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/AuthorTests.java index c8097ecd..aa4ccf8a 100644 --- a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/AuthorTests.java +++ b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/AuthorTests.java @@ -7,6 +7,9 @@ import com.strategyobject.substrateclient.crypto.KeyRing; import com.strategyobject.substrateclient.crypto.PublicKey; import com.strategyobject.substrateclient.rpc.api.*; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import com.strategyobject.substrateclient.scale.ScaleUtils; import com.strategyobject.substrateclient.scale.ScaleWriter; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; @@ -30,7 +33,7 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.Matchers.greaterThan; -import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; @Testcontainers class AuthorTests { @@ -52,12 +55,12 @@ void hasKey() throws Exception { val keyType = "aura"; var result = author.hasKey(publicKey, keyType).get(WAIT_TIMEOUT, TimeUnit.SECONDS); - Assertions.assertFalse(result); + assertFalse(result); author.insertKey(keyType, "alice", publicKey).get(WAIT_TIMEOUT, TimeUnit.SECONDS); result = author.hasKey(publicKey, keyType).get(WAIT_TIMEOUT, TimeUnit.SECONDS); - Assertions.assertTrue(result); + assertTrue(result); } } @@ -66,7 +69,7 @@ void insertKey() throws Exception { try (val wsProvider = connect()) { val author = TestsHelper.createSectionFactory(wsProvider).create(Author.class); - Assertions.assertDoesNotThrow(() -> author.insertKey("aura", + assertDoesNotThrow(() -> author.insertKey("aura", "alice", PublicKey.fromBytes( HexConverter.toBytes("0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"))) @@ -82,7 +85,7 @@ void submitExtrinsic() throws Exception { val genesis = chain.getBlockHash(BlockNumber.GENESIS).get(WAIT_TIMEOUT, TimeUnit.SECONDS); val author = sectionFactory.create(Author.class); - Assertions.assertDoesNotThrow(() -> author.submitExtrinsic(createBalanceTransferExtrinsic(genesis, NONCE.getAndIncrement())) + assertDoesNotThrow(() -> author.submitExtrinsic(createBalanceTransferExtrinsic(genesis, NONCE.getAndIncrement())) .get(WAIT_TIMEOUT, TimeUnit.SECONDS)); } } diff --git a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/ChainTests.java b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/ChainTests.java index 949f3f48..9562b229 100644 --- a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/ChainTests.java +++ b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/ChainTests.java @@ -1,7 +1,7 @@ package com.strategyobject.substrateclient.rpc.api.section; -import com.strategyobject.substrateclient.rpc.api.BlockHash; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockHash; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; import com.strategyobject.substrateclient.transport.ws.ReconnectionPolicy; diff --git a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/StateTests.java b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/StateTests.java index c5ddc094..baad1d09 100644 --- a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/StateTests.java +++ b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/StateTests.java @@ -1,8 +1,8 @@ package com.strategyobject.substrateclient.rpc.api.section; import com.strategyobject.substrateclient.common.convert.HexConverter; -import com.strategyobject.substrateclient.rpc.api.BlockNumber; -import com.strategyobject.substrateclient.rpc.api.StorageKey; +import com.strategyobject.substrateclient.rpc.api.primitives.BlockNumber; +import com.strategyobject.substrateclient.rpc.api.storage.StorageKey; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; import com.strategyobject.substrateclient.transport.ws.ReconnectionPolicy; diff --git a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/SystemTests.java b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/SystemTests.java index dcd3cb10..5448d981 100644 --- a/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/SystemTests.java +++ b/rpc/rpc-api/src/test/java/com/strategyobject/substrateclient/rpc/api/section/SystemTests.java @@ -2,7 +2,7 @@ import com.strategyobject.substrateclient.common.convert.HexConverter; import com.strategyobject.substrateclient.rpc.api.AccountId; -import com.strategyobject.substrateclient.rpc.api.Index; +import com.strategyobject.substrateclient.rpc.api.primitives.Index; import com.strategyobject.substrateclient.tests.containers.SubstrateVersion; import com.strategyobject.substrateclient.tests.containers.TestSubstrateContainer; import com.strategyobject.substrateclient.transport.ws.ReconnectionPolicy; diff --git a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSubscriptionProcessor.java b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSubscriptionProcessor.java index 0fb17ff4..e2003715 100644 --- a/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSubscriptionProcessor.java +++ b/rpc/rpc-codegen/src/main/java/com/strategyobject/substrateclient/rpc/codegen/sections/RpcSubscriptionProcessor.java @@ -56,15 +56,17 @@ protected void callProviderInterface(MethodSpec.Builder methodSpecBuilder, val unsubscribeMethod = String.format(RPC_METHOD_NAME_TEMPLATE, section, annotation.unsubscribeMethod()); val callBackCode = CodeBlock.builder() - .add("$1T<$2T, $3T> $4L = ($5L, $6L) -> { $7N.$8L($5L, ", + .add("$T<$T, $T> $L = ($L, $L) -> { ", BiConsumer.class, Exception.class, RpcObject.class, CALL_BACK_PROXY, CALL_BACK_EX_ARG, - CALL_BACK_ARG, - callbackName, - ACCEPT); + CALL_BACK_ARG) + .beginControlFlow("if ($L != null)", CALL_BACK_EX_ARG) + .addStatement("$N.$L($L, null)", callbackName, ACCEPT, CALL_BACK_EX_ARG) + .nextControlFlow("else") + .add("$N.$L($L, ", callbackName, ACCEPT, CALL_BACK_EX_ARG); if (isCallbackResultVoid(context)) { callBackCode.add("null"); @@ -72,9 +74,13 @@ protected void callProviderInterface(MethodSpec.Builder methodSpecBuilder, callBackCode.add(decoder.apply(callbackParameter, CALL_BACK_ARG)); } - callBackCode.add("); }"); + callBackCode + .add(");") + .endControlFlow() + .addStatement("}"); - methodSpecBuilder.addStatement(callBackCode.build()) + methodSpecBuilder + .addCode(callBackCode.build()) .addStatement(CodeBlock.builder() .add("return $L.$L($S, $S, $L, $L)", PROVIDER_INTERFACE, diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/PairDecoder.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/decoders/PairDecoder.java similarity index 81% rename from rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/PairDecoder.java rename to rpc/src/main/java/com/strategyobject/substrateclient/rpc/decoders/PairDecoder.java index 135a9133..00eefc4e 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/PairDecoder.java +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/decoders/PairDecoder.java @@ -1,14 +1,11 @@ -package com.strategyobject.substrateclient.rpc.api; +package com.strategyobject.substrateclient.rpc.decoders; import com.google.common.base.Preconditions; import com.strategyobject.substrateclient.common.types.tuple.Pair; import com.strategyobject.substrateclient.rpc.DecoderPair; -import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; -import com.strategyobject.substrateclient.rpc.decoders.AbstractDecoder; import com.strategyobject.substrateclient.transport.RpcObject; import lombok.val; -@AutoRegister(types = Pair.class) public class PairDecoder extends AbstractDecoder> { @Override protected Pair decodeNonNull(RpcObject value, DecoderPair[] decoders) { diff --git a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java index 1f0fb00d..92f33e0d 100644 --- a/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java +++ b/rpc/src/main/java/com/strategyobject/substrateclient/rpc/registries/RpcDecoderRegistry.java @@ -4,6 +4,7 @@ import com.strategyobject.substrateclient.common.reflection.Scanner; import com.strategyobject.substrateclient.common.types.Array; import com.strategyobject.substrateclient.common.types.Unit; +import com.strategyobject.substrateclient.common.types.tuple.Pair; import com.strategyobject.substrateclient.rpc.RpcDecoder; import com.strategyobject.substrateclient.rpc.annotation.AutoRegister; import com.strategyobject.substrateclient.rpc.context.RpcDecoderContext; @@ -37,6 +38,7 @@ public RpcDecoderRegistry() { register(new ShortDecoder(), Short.class, short.class); register(new StringDecoder(), String.class); register(new ArrayDecoder(), Array.class); + register(new PairDecoder(), Pair.class); } public void registerAnnotatedFrom(RpcDecoderContextFactory rpcDecoderContextFactory, String... prefixes) {