Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ public class Config {
"wss://asset-hub-westend-rpc.n.dwellir.com",
"wss://asset-hub-westend.rpc.permanence.io")
.toArray(String[]::new);

public static final String[] FREQUENCY_WSS_SERVER = List.of(
"ws://localhost:9944"
).toArray(String[]::new);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.method5.jot.examples.extrinsic;

import com.method5.jot.entity.DispatchError;
import com.method5.jot.events.EventRecord;
import com.method5.jot.examples.Config;
import com.method5.jot.extrinsic.ExtrinsicResult;
import com.method5.jot.extrinsic.call.Call;
import com.method5.jot.query.model.AccountId;
import com.method5.jot.rpc.PolkadotWs;
Expand All @@ -11,14 +14,15 @@
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.List;

public class BalancesTransferAllowDeathExample extends ExampleBase {
private static final Logger logger = LoggerFactory.getLogger(BalancesTransferAllowDeathExample.class);

public static void main(String[] args) throws Exception {
Wallet wallet = Wallet.fromMnemonic(Config.MNEMONIC_PHRASE);

try (PolkadotWs api = new PolkadotWs(Config.WSS_SERVER, 10000)) {
try (PolkadotWs api = new PolkadotWs(Config.FREQUENCY_WSS_SERVER, 10000)) {
execute(api, wallet.getSigner());
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.method5.jot.examples.extrinsic;

import com.method5.jot.events.EventRecord;
import com.method5.jot.examples.Config;
import com.method5.jot.extrinsic.ExtrinsicResult;
import com.method5.jot.extrinsic.call.Call;
import com.method5.jot.query.model.AccountId;
import com.method5.jot.rpc.PolkadotWs;
import com.method5.jot.signing.SigningProvider;
import com.method5.jot.wallet.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.List;

public class BalancesTransferAllowDeathSignAndWaitForResultsExample {
private static final Logger logger = LoggerFactory.getLogger(BalancesTransferAllowDeathSignAndWaitForResultsExample.class);

public static void main(String[] args) throws Exception {
Wallet wallet = Wallet.fromMnemonic(Config.MNEMONIC_PHRASE);

try (PolkadotWs api = new PolkadotWs(Config.FREQUENCY_WSS_SERVER, 10000)) {
execute(api, wallet.getSigner());
}
}

public static void execute(PolkadotWs api, SigningProvider signingProvider) throws Exception {
logger.info("Balances Transfer Allow Death (Using signAndWaitForResults) Example");
logger.info("------------------------");

// Destination address
AccountId destination = AccountId.fromSS58("13NHcoGFJsHJoCYVsJrrv2ygLtz2XJSR17KrnA9QTNYz3Zkz");
// Amount
BigDecimal amount = new BigDecimal("0.001");

Call call = api.tx().balances().transferAllowDeath(destination, amount);

ExtrinsicResult result = call.signAndWaitForResults(signingProvider);

List<EventRecord> eventRecordList = result.getEvents();
for (EventRecord eventRecord : eventRecordList) {
logger.info("Event: " + eventRecord.method());
}

String hash = result.getHash();


logger.info("Extrinsic hash: {}", hash);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.method5.jot.examples.extrinsic;

import com.method5.jot.events.EventRecord;
import com.method5.jot.examples.Config;
import com.method5.jot.extrinsic.ExtrinsicResult;
import com.method5.jot.extrinsic.call.Call;
import com.method5.jot.rpc.PolkadotWs;
import com.method5.jot.signing.SigningProvider;
import com.method5.jot.util.HexUtil;
import com.method5.jot.wallet.Wallet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;

public class CreateMsaExample {
private static final Logger logger = LoggerFactory.getLogger(CreateMsaExample.class);

public static void main(String[] args) throws Exception {
Wallet alice = Wallet.fromSr25519Seed(HexUtil.hexToBytes("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this changed from the Mnemonic?


try (PolkadotWs api = new PolkadotWs(Config.FREQUENCY_WSS_SERVER, 10000)) {
execute(api, alice.getSigner());
}
}

public static void execute(PolkadotWs api, SigningProvider signingProvider) throws Exception {
logger.info("Create MSA Example");
logger.info("------------------------");

Call call = api.tx().msa().createMsa();

ExtrinsicResult result = call.signAndWaitForResults(signingProvider);

List<EventRecord> eventRecordList = result.getEvents();
for (EventRecord eventRecord : eventRecordList) {
logger.info("Event: " + eventRecord.method());
}

String hash = result.getHash();

logger.info("Extrinsic hash: {}", hash);

//Now try and get an error by sending the createMsa transaction again

ExtrinsicResult failure = call.signAndWaitForResults(signingProvider);

logger.info("Extrinsic dispatch error: " + failure.getError());
}
}
6 changes: 6 additions & 0 deletions jot/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -410,5 +410,11 @@
<version>5.20.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<version>2.0.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
29 changes: 27 additions & 2 deletions jot/src/main/java/com/method5/jot/entity/DispatchError.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import com.method5.jot.metadata.CallIndexResolver;
import com.method5.jot.metadata.RuntimeTypeDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;

/**
* DispatchError — class for dispatch error in the Jot SDK. Provides types and data models.
*/
public class DispatchError {
private static final Logger logger = LoggerFactory.getLogger(DispatchError.class);
public enum Kind {
MODULE,
NAMED,
Expand All @@ -21,6 +25,14 @@ public enum Kind {
private final int errorCode;
private final String name;

public String getName() {
return name;
}

public int getModuleIndex() {
return moduleIndex;
}

private DispatchError(Kind kind, int moduleIndex, int errorCode, String name) {
this.kind = kind;
this.moduleIndex = moduleIndex;
Expand Down Expand Up @@ -59,9 +71,22 @@ public static DispatchError decode(RuntimeTypeDecoder.TypeAndValue tv, CallIndex

if ("Module".equals(variant)) {
Map<String, Object> field0 = (Map<String, Object>) outer.get("field0");

if (field0 != null) {
int index = (int) field0.getOrDefault("index", -1);
int error = (int) field0.getOrDefault("error", -1);
int index;
int error;
if(field0.getOrDefault("index", -1) instanceof Byte indexByte) {
index = ((Number) indexByte).intValue();
} else {
index = (int) field0.getOrDefault("index", -1);
}
//Is there a better way to do this?
if(field0.get("error") instanceof ArrayList errorList) {
error = ((Number) errorList.getFirst()).intValue();
} else {
error = (int) field0.getOrDefault("error", -1);
}

String name = resolver != null ? resolver.getModuleError(index, error) : "Unknown module error";
return module(index, error, name);
}
Expand Down
28 changes: 28 additions & 0 deletions jot/src/main/java/com/method5/jot/extrinsic/call/AccountId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.method5.jot.extrinsic.call;

import java.util.Arrays;

public class AccountId {
private final byte[] publicKeyBytes;

public AccountId(byte[] publicKeyBytes) {
this.publicKeyBytes = publicKeyBytes;
}

@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof AccountId)) return false;
AccountId that = (AccountId) other;
return Arrays.equals(this.publicKeyBytes, that.publicKeyBytes);
}

@Override
public int hashCode() {
return Arrays.hashCode(publicKeyBytes);
}

public static AccountId fromBytes(byte[] data) {
return new AccountId(data);
}
}
12 changes: 12 additions & 0 deletions jot/src/main/java/com/method5/jot/extrinsic/call/EventClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.method5.jot.extrinsic.call;

import com.method5.jot.metadata.RuntimeTypeDecoder;

import java.util.Map;

public interface EventClass<T> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should follow a "Mapper" concept that works on a generic and remove the static on the interface. I'm actually shocked that worked. But this is close, just make it <T> T map(...)

// Factory method that must create a new instance
static <T> T create(Map<String, RuntimeTypeDecoder.TypeAndValue> attributes) {
throw new UnsupportedOperationException("Must be implemented by subclass");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.method5.jot.extrinsic.call;

public interface ExtrinsicError {}

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.method5.jot.extrinsic.call;

public class KeyAlreadyRegisteredError implements ExtrinsicError{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So rather than make a class per Error just use an enum. Something with a surrogate key called something like palletValue and compare against that in order to figure out what enu, value to return. See our fromXXX static methods on lot's of enums. You can still use a marker interface like ExtrinsicError as well but we'd want to "namespace them" by pallet to not what a gigantic enum for everything. Could even make a MsaExtrinsiveError extend the ExtrinsicError marker interface.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.method5.jot.extrinsic.call;

import java.math.BigInteger;
import java.util.Objects;

public class MessageSourceId {
private final BigInteger value;

public MessageSourceId(BigInteger value) {
this.value = value;
}

public BigInteger getValue() {
return value;
}

@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
MessageSourceId that = (MessageSourceId) other;
return Objects.equals(value, that.value);
}

@Override
public int hashCode() {
return Objects.hash(value);
}
}
60 changes: 60 additions & 0 deletions jot/src/main/java/com/method5/jot/extrinsic/call/MsaPallet.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.method5.jot.extrinsic.call;

import com.method5.jot.metadata.RuntimeTypeDecoder;
import com.method5.jot.rpc.Api;
import com.method5.jot.rpc.CallOrQuery;
import com.method5.jot.scale.ScaleWriter;

import java.math.BigInteger;
import java.util.*;

public class MsaPallet extends CallOrQuery {
public MsaPallet(Api api) {
super(api);
}

public Call createMsa() {
return new Call(api, createMsaWriter(
getResolver().resolveCallIndex("Msa", "create")
));
}

private byte[] createMsaWriter(byte[] callIndex) {
ScaleWriter writer = new ScaleWriter();
writer.writeBytes(callIndex);
return writer.toByteArray();
}

public static class MsaCreated implements EventClass<MsaCreated>{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This EventClass is probably better modeled as MsaCreatedMapper implements Mapper<MsaCreated> (this is effectively the "ScaleReader" concept but since we have metadata we don't need to do as much) and make a mapper interface (with no static) that has a "map" or "create" method that only works. This will also would lift this static inner class out

private final MessageSourceId msaId;
private final AccountId accountId;

public MsaCreated(MessageSourceId msaId, AccountId accountId) {
this.msaId = msaId;
this.accountId = accountId;
}

public MessageSourceId getMsaId() {
return msaId;
}

public AccountId getAccountId() {
return accountId;
}

public static MsaCreated create(Map<String, RuntimeTypeDecoder.TypeAndValue> attributes) {
BigInteger msaIdValue = new BigInteger(attributes.get("MessageSourceId").getValue().toString());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is "MessageSourceId" cause this toString seems pretty dangerous

MessageSourceId msaId = new MessageSourceId(msaIdValue);
ArrayList<Byte> byteList = (ArrayList<Byte>) ((HashMap) attributes.get("T::AccountId").getValue()).get("field1");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should just use List to avoid class cast exceptons if the impl changes. Also this field1 business is annoying. What type is this in the metadata file, is this a tuple of some kind?

byte[] accountIdValue = new byte[byteList.size()];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (int i = 0; i < byteList.size(); i++) {
accountIdValue[i] = byteList.get(i);
}
AccountId accountId = new AccountId(accountIdValue);

return new MsaCreated(msaId, accountId);
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class Transaction {
protected BalancesPallet balances;
protected ConvictionVotingPallet convictionVoting;
protected MultisigPallet multisig;
protected MsaPallet msa;
protected StakingPallet staking;
protected SystemPallet system;
protected UtilityPallet utility;
Expand All @@ -20,6 +21,7 @@ public Transaction(Api api) {
balances = new BalancesPallet(api);
convictionVoting = new ConvictionVotingPallet(api);
multisig = new MultisigPallet(api);
msa = new MsaPallet(api);
staking = new StakingPallet(api);
system = new SystemPallet(api);
utility = new UtilityPallet(api);
Expand All @@ -37,6 +39,10 @@ public MultisigPallet multisig() {
return multisig;
}

public MsaPallet msa() {
return msa;
}

public StakingPallet staking() {
return staking;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class CallIndexResolver {
private final Map<String, byte[]> callMap = new HashMap<>();
private final Map<Integer, String> moduleIndexToName = new HashMap<>();
private final Map<String, List<String>> moduleFunctions = new HashMap<>();
private final Map<String, List<String>> moduleEvents = new HashMap<>();
public final Map<String, List<String>> moduleEvents = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no publics, also why?

private final Map<String, Map<Integer, String>> moduleErrors = new HashMap<>();

public CallIndexResolver() {}
Expand Down
Loading