diff --git a/explorer-ui/src/contexts/chainNames.test.ts b/explorer-ui/src/contexts/chainNames.test.ts index ee1e64b..1050b92 100644 --- a/explorer-ui/src/contexts/chainNames.test.ts +++ b/explorer-ui/src/contexts/chainNames.test.ts @@ -21,5 +21,5 @@ test("Shibuya chain info", () => { }) test("watr testnet chain info", () => { - expect(resolveInfoFromName("watr network testnet")).toEqual("rococoWatr") + expect(resolveInfoFromName("watr testnet")).toEqual("rococoWatr") }) diff --git a/explorer-ui/src/contexts/chainNames.ts b/explorer-ui/src/contexts/chainNames.ts index 39f8a44..3a05796 100644 --- a/explorer-ui/src/contexts/chainNames.ts +++ b/explorer-ui/src/contexts/chainNames.ts @@ -20,7 +20,7 @@ const names = [ endpoint: "watr" }, { - chain: "watr network testnet", + chain: "watr testnet", endpoint: "rococoWatr" }, { diff --git a/squid-ink/src/chain-config.ts b/squid-ink/src/chain-config.ts index ca3945d..826d9ce 100644 --- a/squid-ink/src/chain-config.ts +++ b/squid-ink/src/chain-config.ts @@ -41,11 +41,11 @@ const chainConfig: Record<string, ChainConfig> = { ss58Format: 19, token: { tokenDecimals: 18, - tokenSymbol: "WATRD", + tokenSymbol: "WATR", }, }, watr_test: { - name: "Watr Network Testnet", + name: "Watr Testnet", ss58Format: 19, token: { tokenDecimals: 18, diff --git a/squid-ink/src/chains/watrtestnet/normalised-types/calls.ts b/squid-ink/src/chains/watrtestnet/normalised-types/calls.ts new file mode 100644 index 0000000..19d246e --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/normalised-types/calls.ts @@ -0,0 +1,27 @@ +import * as ss58 from "@subsquid/ss58"; +import { ResolvedContractsCallCall } from "chains/normalised-return-types"; +import { ss58Format } from "../../../chain-config"; +import { ContractsCallCall } from "../types/calls"; + +export class NormalisedContractsCallCall extends ContractsCallCall { + resolve(): ResolvedContractsCallCall { + if (this.isWatrNodeV1000) { + const { dest, value, gasLimit, storageDepositLimit, data } = + this.asWatrNodeV1000; + // TODO: Ensure proper support of MultiAddress + if (dest.__kind === "Index") { + throw new Error("Multi-address of type Index is not supported!"); + } + return { + contractAddress: ss58.codec(ss58Format).encode(dest.value), + value, + gasLimit: { + refTime: gasLimit, + }, + storageDepositLimit, + data, + }; + } + throw new Error("No Runtime version found"); + } +} diff --git a/squid-ink/src/chains/watrtestnet/normalised-types/events.ts b/squid-ink/src/chains/watrtestnet/normalised-types/events.ts new file mode 100644 index 0000000..89907cf --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/normalised-types/events.ts @@ -0,0 +1,189 @@ +// eslint-disable-next-line eslint-comments/disable-enable-pair +/* eslint-disable max-classes-per-file */ +import * as ss58 from "@subsquid/ss58"; +import { toHex } from "@subsquid/util-internal-hex"; +import { + ResolvedBalancesEndowedEvent, + ResolvedBalancesReservedEvent, + ResolvedBalancesTransferEvent, + ResolvedBalancesWithdrawEvent, + ResolvedContractEmittedEvent, + ResolvedContractCodeRemovedEvent, + ResolvedContractsCodeStoredEvent, + ResolvedContractsCodeUpdatedEvent, + ResolvedContractsInstantiatedEvent, + ResolvedContractTerminatedEvent, + ResolvedNewAccountEvent, +} from "chains/normalised-return-types"; +import { ss58Format } from "../../../chain-config"; +import { + BalancesEndowedEvent, + BalancesReservedEvent, + BalancesTransferEvent, + BalancesWithdrawEvent, + ContractsCodeRemovedEvent, + ContractsCodeStoredEvent, + ContractsContractCodeUpdatedEvent, + ContractsContractEmittedEvent, + ContractsInstantiatedEvent, + ContractsTerminatedEvent, + SystemNewAccountEvent, +} from "../types/events"; + +export class NormalisedBalancesTransferEvent extends BalancesTransferEvent { + resolve(): ResolvedBalancesTransferEvent { + if (this.isWatrNodeV1000) { + const { from, to, amount } = this.asWatrNodeV1000; + return { + from: ss58.codec(ss58Format).encode(from), + to: ss58.codec(ss58Format).encode(to), + amount, + }; + } + throw new Error( + "No runtime version found while decoding [BalancesTransferEvent]" + ); + } +} + +export class NormalisedBalancesEndowedEvent extends BalancesEndowedEvent { + resolve(): ResolvedBalancesEndowedEvent { + if (this.isWatrNodeV1000) { + const { account, freeBalance } = this.asWatrNodeV1000; + return { + account: ss58.codec(ss58Format).encode(account), + freeBalance, + }; + } + throw new Error( + "No runtime version found while decoding [BalancesEndowedEvent]" + ); + } +} + +export class NormalisedBalancesWithdrawEvent extends BalancesWithdrawEvent { + resolve(): ResolvedBalancesWithdrawEvent { + if (this.isWatrNodeV1000) { + const { who, amount } = this.asWatrNodeV1000; + return { + account: ss58.codec(ss58Format).encode(who), + amount, + }; + } + throw new Error( + "No runtime version found while decoding [BalancesWithdrawEvent]" + ); + } +} + +export class NormalisedBalancesReservedEvent extends BalancesReservedEvent { + resolve(): ResolvedBalancesReservedEvent { + if (this.isWatrNodeV1000) { + const { who, amount } = this.asWatrNodeV1000; + return { + account: ss58.codec(ss58Format).encode(who), + amount, + }; + } + throw new Error( + "No runtime version found while decoding [BalancesReservedEvent]" + ); + } +} + +export class NormalisedContractsCodeRemovedEvent extends ContractsCodeRemovedEvent { + resolve(): ResolvedContractCodeRemovedEvent { + if (this.isWatrNodeV1000) { + const { codeHash } = this.asWatrNodeV1000; + return { codeHash: toHex(codeHash) }; + } + throw new Error( + "No runtime version found while decoding [ContractsCodeRemovedEvent]" + ); + } +} + +export class NormalisedContractsInstantiatedEvent extends ContractsInstantiatedEvent { + resolve(): ResolvedContractsInstantiatedEvent { + if (this.isWatrNodeV1000) { + const { deployer, contract } = this.asWatrNodeV1000; + return { + deployer: ss58.codec(ss58Format).encode(deployer), + contract: ss58.codec(ss58Format).encode(contract), + }; + } + throw new Error( + "No runtime version found while decoding [ContractsInstantiatedEvent]" + ); + } +} + +export class NormalisedContractsCodeStoredEvent extends ContractsCodeStoredEvent { + resolve(): ResolvedContractsCodeStoredEvent { + if (this.isWatrNodeV1000) { + const { codeHash } = this.asWatrNodeV1000; + return { codeHash: toHex(codeHash) }; + } + throw new Error( + "No runtime version found while decoding [ContractsCodeStoredEvent]" + ); + } +} + +export class NormalisedContractsCodeUpdatedEvent extends ContractsContractCodeUpdatedEvent { + resolve(): ResolvedContractsCodeUpdatedEvent { + if (this.isWatrNodeV1000) { + const { contract, newCodeHash, oldCodeHash } = this.asWatrNodeV1000; + return { + contract: ss58.codec(ss58Format).encode(contract), + newCodeHash: toHex(newCodeHash), + oldCodeHash: toHex(oldCodeHash), + }; + } + throw new Error( + "No runtime version found while decoding [ContractsContractCodeUpdatedEvent]" + ); + } +} + +export class NormalisedContractEmittedEvent extends ContractsContractEmittedEvent { + resolve(): ResolvedContractEmittedEvent { + if (this.isWatrNodeV1000) { + const { contract, data } = this.asWatrNodeV1000; + return { contract: ss58.codec(ss58Format).encode(contract), data }; + } + throw new Error( + "No runtime version found while decoding [ContractsContractEmittedEvent]" + ); + } +} + +export class NormalisedContractTerminatedEvent extends ContractsTerminatedEvent { + resolve(): ResolvedContractTerminatedEvent { + if (this.isWatrNodeV1000) { + const { contract, beneficiary } = this.asWatrNodeV1000 as { + contract: Uint8Array; + beneficiary: Uint8Array; + }; + return { + contract: ss58.codec(ss58Format).encode(contract), + beneficiary: ss58.codec(ss58Format).encode(beneficiary), + }; + } + throw new Error( + "No runtime version found while decoding [ContractsTerminatedEvent]" + ); + } +} + +export class NormalisedSystemNewAccountEvent extends SystemNewAccountEvent { + resolve(): ResolvedNewAccountEvent { + if (this.isWatrNodeV1000) { + const { account } = this.asWatrNodeV1000; + return { account: ss58.codec(ss58Format).encode(account) }; + } + throw new Error( + "No runtime version found while decoding [SystemNewAccountEvent]" + ); + } +} diff --git a/squid-ink/src/chains/watrtestnet/normalised-types/index.ts b/squid-ink/src/chains/watrtestnet/normalised-types/index.ts new file mode 100644 index 0000000..8eaa778 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/normalised-types/index.ts @@ -0,0 +1,3 @@ +export * from "./calls"; +export * from "./events"; +export * from "./storage"; diff --git a/squid-ink/src/chains/watrtestnet/normalised-types/storage.ts b/squid-ink/src/chains/watrtestnet/normalised-types/storage.ts new file mode 100644 index 0000000..3b710c8 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/normalised-types/storage.ts @@ -0,0 +1,90 @@ +import assert from "assert"; +import * as ss58 from "@subsquid/ss58"; +import { decodeHex } from "@subsquid/util-internal-hex"; +import { ResolvedContractInfoOfStorage } from "chains/normalised-return-types"; +import { + BalancesAccountStorage, + ContractsCodeStorageStorage, + ContractsContractInfoOfStorage, + ContractsOwnerInfoOfStorage, + SystemAccountStorage, +} from "../types/storage"; +import { + AccountData, + AccountInfo, + OwnerInfo, + PrefabWasmModule, +} from "../types/watrNodeV1000"; +import { ss58Format } from "../../../chain-config"; + +export class NormalisedSystemAccountStorage extends SystemAccountStorage { + async get(accountId: string): Promise<AccountInfo> { + assert(this.isExists); + if (this.isWatrNodeV1000) { + return this.getAsWatrNodeV1000(ss58.codec(ss58Format).decode(accountId)); + } + throw new Error("No Runtime version found"); + } +} + +export class NormalisedBalancesAccountStorage extends BalancesAccountStorage { + async get(accountId: string): Promise<AccountData> { + assert(this.isExists); + if (this.isWatrNodeV1000) { + return this.getAsWatrNodeV1000(ss58.codec(ss58Format).decode(accountId)); + } + throw new Error("No Runtime version found"); + } +} + +export class NormalisedContractInfoOfStorage extends ContractsContractInfoOfStorage { + async get(accountId: string): Promise<ResolvedContractInfoOfStorage> { + assert(this.isExists); + let info: ResolvedContractInfoOfStorage | undefined; + if (this.isWatrNodeV1000) { + info = await this.getAsWatrNodeV1000( + ss58.codec(ss58Format).decode(accountId) + ); + } else { + throw new Error("No Runtime version found"); + } + if (info) { + return info; + } + throw new Error( + `ContractInfoOf not found in storage for accountId [${accountId}]` + ); + } +} + +export class NormalisedCodeStorageStorage extends ContractsCodeStorageStorage { + async get(key: string): Promise<PrefabWasmModule> { + assert(this.isExists); + let info: PrefabWasmModule | undefined; + if (this.isWatrNodeV1000) { + info = await this.getAsWatrNodeV1000(decodeHex(key)); + } else { + throw new Error("No Runtime version found"); + } + if (info) { + return info; + } + throw new Error(`CodeStorage not found in storage for key [${key}]`); + } +} + +export class NormalisedOwnerInfoOfStorage extends ContractsOwnerInfoOfStorage { + async get(key: string): Promise<OwnerInfo> { + assert(this.isExists); + let info: OwnerInfo | undefined; + if (this.isWatrNodeV1000) { + info = await this.getAsWatrNodeV1000(decodeHex(key)); + } else { + throw new Error("No Runtime version found"); + } + if (info) { + return info; + } + throw new Error(`CodeStorage not found in storage for key [${key}]`); + } +} diff --git a/squid-ink/src/chains/watrtestnet/types/calls.ts b/squid-ink/src/chains/watrtestnet/types/calls.ts new file mode 100644 index 0000000..ee7daf5 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/types/calls.ts @@ -0,0 +1,62 @@ +import assert from 'assert' +import {Chain, ChainContext, CallContext, Call, Result, Option} from './support' +import * as watrNodeV1000 from './watrNodeV1000' + +export class ContractsCallCall { + private readonly _chain: Chain + private readonly call: Call + + constructor(ctx: CallContext) + constructor(ctx: ChainContext, call: Call) + constructor(ctx: CallContext, call?: Call) { + call = call || ctx.call + assert(call.name === 'Contracts.call') + this._chain = ctx._chain + this.call = call + } + + /** + * Makes a call to an account, optionally transferring some balance. + * + * # Parameters + * + * * `dest`: Address of the contract to call. + * * `value`: The balance to transfer from the `origin` to `dest`. + * * `gas_limit`: The gas limit enforced when executing the constructor. + * * `storage_deposit_limit`: The maximum amount of balance that can be charged from the + * caller to pay for the storage consumed. + * * `data`: The input data to pass to the contract. + * + * * If the account is a smart-contract account, the associated code will be + * executed and any value will be transferred. + * * If the account is a regular account, any value will be transferred. + * * If no account exists and the call value is not less than `existential_deposit`, + * a regular account will be created and any value will be transferred. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getCallHash('Contracts.call') === 'd96c8a6656d7a4d6af6d5d0d51dd36e041c9ea8a92a7ead343d711addd74780f' + } + + /** + * Makes a call to an account, optionally transferring some balance. + * + * # Parameters + * + * * `dest`: Address of the contract to call. + * * `value`: The balance to transfer from the `origin` to `dest`. + * * `gas_limit`: The gas limit enforced when executing the constructor. + * * `storage_deposit_limit`: The maximum amount of balance that can be charged from the + * caller to pay for the storage consumed. + * * `data`: The input data to pass to the contract. + * + * * If the account is a smart-contract account, the associated code will be + * executed and any value will be transferred. + * * If the account is a regular account, any value will be transferred. + * * If no account exists and the call value is not less than `existential_deposit`, + * a regular account will be created and any value will be transferred. + */ + get asWatrNodeV1000(): {dest: watrNodeV1000.MultiAddress, value: bigint, gasLimit: bigint, storageDepositLimit: (bigint | undefined), data: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeCall(this.call) + } +} diff --git a/squid-ink/src/chains/watrtestnet/types/events.ts b/squid-ink/src/chains/watrtestnet/types/events.ts new file mode 100644 index 0000000..a90eda7 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/types/events.ts @@ -0,0 +1,331 @@ +import assert from 'assert' +import {Chain, ChainContext, EventContext, Event, Result, Option} from './support' + +export class BalancesEndowedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Balances.Endowed') + this._chain = ctx._chain + this.event = event + } + + /** + * An account was created with some free balance. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Balances.Endowed') === '75951f685df19cbb5fdda09cf928a105518ceca9576d95bd18d4fac8802730ca' + } + + /** + * An account was created with some free balance. + */ + get asWatrNodeV1000(): {account: Uint8Array, freeBalance: bigint} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class BalancesReservedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Balances.Reserved') + this._chain = ctx._chain + this.event = event + } + + /** + * Some balance was reserved (moved from free to reserved). + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Balances.Reserved') === 'e84a34a6a3d577b31f16557bd304282f4fe4cbd7115377f4687635dc48e52ba5' + } + + /** + * Some balance was reserved (moved from free to reserved). + */ + get asWatrNodeV1000(): {who: Uint8Array, amount: bigint} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class BalancesTransferEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Balances.Transfer') + this._chain = ctx._chain + this.event = event + } + + /** + * Transfer succeeded. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Balances.Transfer') === '0ffdf35c495114c2d42a8bf6c241483fd5334ca0198662e14480ad040f1e3a66' + } + + /** + * Transfer succeeded. + */ + get asWatrNodeV1000(): {from: Uint8Array, to: Uint8Array, amount: bigint} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class BalancesWithdrawEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Balances.Withdraw') + this._chain = ctx._chain + this.event = event + } + + /** + * Some amount was withdrawn from the account (e.g. for transaction fees). + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Balances.Withdraw') === 'e84a34a6a3d577b31f16557bd304282f4fe4cbd7115377f4687635dc48e52ba5' + } + + /** + * Some amount was withdrawn from the account (e.g. for transaction fees). + */ + get asWatrNodeV1000(): {who: Uint8Array, amount: bigint} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsCodeRemovedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.CodeRemoved') + this._chain = ctx._chain + this.event = event + } + + /** + * A code with the specified hash was removed. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.CodeRemoved') === '9e5c86c297bd88fae31bc40119e44695818ddc3ab8842b90daeb12771005c70d' + } + + /** + * A code with the specified hash was removed. + */ + get asWatrNodeV1000(): {codeHash: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsCodeStoredEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.CodeStored') + this._chain = ctx._chain + this.event = event + } + + /** + * Code with the specified hash has been stored. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.CodeStored') === '9e5c86c297bd88fae31bc40119e44695818ddc3ab8842b90daeb12771005c70d' + } + + /** + * Code with the specified hash has been stored. + */ + get asWatrNodeV1000(): {codeHash: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsContractCodeUpdatedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.ContractCodeUpdated') + this._chain = ctx._chain + this.event = event + } + + /** + * A contract's code was updated. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.ContractCodeUpdated') === 'f9de6decda4961d31d7cf59e3f8acd4849a220323ebabbb036464d999de54c18' + } + + /** + * A contract's code was updated. + */ + get asWatrNodeV1000(): {contract: Uint8Array, newCodeHash: Uint8Array, oldCodeHash: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsContractEmittedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.ContractEmitted') + this._chain = ctx._chain + this.event = event + } + + /** + * A custom event emitted by the contract. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.ContractEmitted') === '7f28393268795b9a97f05e82911cdcc4200d99e9968c1ab6a564f949f753b929' + } + + /** + * A custom event emitted by the contract. + */ + get asWatrNodeV1000(): {contract: Uint8Array, data: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsInstantiatedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.Instantiated') + this._chain = ctx._chain + this.event = event + } + + /** + * Contract deployed by address at the specified address. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.Instantiated') === '20f9f9057a4149f58eb48c00359f9800a42b51d4d2168437dfcce668c27a8d37' + } + + /** + * Contract deployed by address at the specified address. + */ + get asWatrNodeV1000(): {deployer: Uint8Array, contract: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class ContractsTerminatedEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'Contracts.Terminated') + this._chain = ctx._chain + this.event = event + } + + /** + * Contract has been removed. + * + * # Note + * + * The only way for a contract to be removed and emitting this event is by calling + * `seal_terminate`. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('Contracts.Terminated') === '8e0b376b4821223ecd835a0ae76a615e7aa14158260ff9c7f87220449d98175b' + } + + /** + * Contract has been removed. + * + * # Note + * + * The only way for a contract to be removed and emitting this event is by calling + * `seal_terminate`. + */ + get asWatrNodeV1000(): {contract: Uint8Array, beneficiary: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} + +export class SystemNewAccountEvent { + private readonly _chain: Chain + private readonly event: Event + + constructor(ctx: EventContext) + constructor(ctx: ChainContext, event: Event) + constructor(ctx: EventContext, event?: Event) { + event = event || ctx.event + assert(event.name === 'System.NewAccount') + this._chain = ctx._chain + this.event = event + } + + /** + * A new account was created. + */ + get isWatrNodeV1000(): boolean { + return this._chain.getEventHash('System.NewAccount') === '7fb7672b764b0a4f0c4910fddefec0709628843df7ad0073a97eede13c53ca92' + } + + /** + * A new account was created. + */ + get asWatrNodeV1000(): {account: Uint8Array} { + assert(this.isWatrNodeV1000) + return this._chain.decodeEvent(this.event) + } +} diff --git a/squid-ink/src/chains/watrtestnet/types/storage.ts b/squid-ink/src/chains/watrtestnet/types/storage.ts new file mode 100644 index 0000000..98542b3 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/types/storage.ts @@ -0,0 +1,278 @@ +import assert from 'assert' +import {Block, Chain, ChainContext, BlockContext, Result, Option} from './support' +import * as watrNodeV1000 from './watrNodeV1000' + +export class BalancesAccountStorage { + private readonly _chain: Chain + private readonly blockHash: string + + constructor(ctx: BlockContext) + constructor(ctx: ChainContext, block: Block) + constructor(ctx: BlockContext, block?: Block) { + block = block || ctx.block + this.blockHash = block.hash + this._chain = ctx._chain + } + + /** + * The Balances pallet example of storing the balance of an account. + * + * # Example + * + * ```rust + * impl pallet_balances::Config for Runtime { + * type AccountStore = StorageMapShim<Self::Account<Runtime>, frame_system::Provider<Runtime>, AccountId, Self::AccountData<Balance>> + * } + * ``` + * + * You can also store the balance of an account in the `System` pallet. + * + * # Example + * + * ```rust + * impl pallet_balances::Config for Runtime { + * type AccountStore = System + * } + * ``` + * + * But this comes with tradeoffs, storing account balances in the system pallet stores + * `frame_system` data alongside the account data contrary to storing account balances in the + * `Balances` pallet, which uses a `StorageMap` to store balances data only. + * NOTE: This is only used in the case that this pallet is used to store balances. + */ + get isWatrNodeV1000() { + return this._chain.getStorageItemTypeHash('Balances', 'Account') === '0b3b4bf0dd7388459eba461bc7c3226bf58608c941710a714e02f33ec0f91e78' + } + + /** + * The Balances pallet example of storing the balance of an account. + * + * # Example + * + * ```rust + * impl pallet_balances::Config for Runtime { + * type AccountStore = StorageMapShim<Self::Account<Runtime>, frame_system::Provider<Runtime>, AccountId, Self::AccountData<Balance>> + * } + * ``` + * + * You can also store the balance of an account in the `System` pallet. + * + * # Example + * + * ```rust + * impl pallet_balances::Config for Runtime { + * type AccountStore = System + * } + * ``` + * + * But this comes with tradeoffs, storing account balances in the system pallet stores + * `frame_system` data alongside the account data contrary to storing account balances in the + * `Balances` pallet, which uses a `StorageMap` to store balances data only. + * NOTE: This is only used in the case that this pallet is used to store balances. + */ + async getAsWatrNodeV1000(key: Uint8Array): Promise<watrNodeV1000.AccountData> { + assert(this.isWatrNodeV1000) + return this._chain.getStorage(this.blockHash, 'Balances', 'Account', key) + } + + async getManyAsWatrNodeV1000(keys: Uint8Array[]): Promise<(watrNodeV1000.AccountData)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Balances', 'Account', keys.map(k => [k])) + } + + async getAllAsWatrNodeV1000(): Promise<(watrNodeV1000.AccountData)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Balances', 'Account') + } + + /** + * Checks whether the storage item is defined for the current chain version. + */ + get isExists(): boolean { + return this._chain.getStorageItemTypeHash('Balances', 'Account') != null + } +} + +export class ContractsCodeStorageStorage { + private readonly _chain: Chain + private readonly blockHash: string + + constructor(ctx: BlockContext) + constructor(ctx: ChainContext, block: Block) + constructor(ctx: BlockContext, block?: Block) { + block = block || ctx.block + this.blockHash = block.hash + this._chain = ctx._chain + } + + /** + * A mapping between an original code hash and instrumented wasm code, ready for execution. + */ + get isWatrNodeV1000() { + return this._chain.getStorageItemTypeHash('Contracts', 'CodeStorage') === '1d41f869264eec7411828c1a845cdbad1a39455691f254f6bfead6b3102145ab' + } + + /** + * A mapping between an original code hash and instrumented wasm code, ready for execution. + */ + async getAsWatrNodeV1000(key: Uint8Array): Promise<watrNodeV1000.PrefabWasmModule | undefined> { + assert(this.isWatrNodeV1000) + return this._chain.getStorage(this.blockHash, 'Contracts', 'CodeStorage', key) + } + + async getManyAsWatrNodeV1000(keys: Uint8Array[]): Promise<(watrNodeV1000.PrefabWasmModule | undefined)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'CodeStorage', keys.map(k => [k])) + } + + async getAllAsWatrNodeV1000(): Promise<(watrNodeV1000.PrefabWasmModule)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'CodeStorage') + } + + /** + * Checks whether the storage item is defined for the current chain version. + */ + get isExists(): boolean { + return this._chain.getStorageItemTypeHash('Contracts', 'CodeStorage') != null + } +} + +export class ContractsContractInfoOfStorage { + private readonly _chain: Chain + private readonly blockHash: string + + constructor(ctx: BlockContext) + constructor(ctx: ChainContext, block: Block) + constructor(ctx: BlockContext, block?: Block) { + block = block || ctx.block + this.blockHash = block.hash + this._chain = ctx._chain + } + + /** + * The code associated with a given account. + * + * TWOX-NOTE: SAFE since `AccountId` is a secure hash. + */ + get isWatrNodeV1000() { + return this._chain.getStorageItemTypeHash('Contracts', 'ContractInfoOf') === 'ca1ad2ae4b550883411d45c2158af4f3e2a0bde306e44674a586527ce222bcf3' + } + + /** + * The code associated with a given account. + * + * TWOX-NOTE: SAFE since `AccountId` is a secure hash. + */ + async getAsWatrNodeV1000(key: Uint8Array): Promise<watrNodeV1000.RawContractInfo | undefined> { + assert(this.isWatrNodeV1000) + return this._chain.getStorage(this.blockHash, 'Contracts', 'ContractInfoOf', key) + } + + async getManyAsWatrNodeV1000(keys: Uint8Array[]): Promise<(watrNodeV1000.RawContractInfo | undefined)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'ContractInfoOf', keys.map(k => [k])) + } + + async getAllAsWatrNodeV1000(): Promise<(watrNodeV1000.RawContractInfo)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'ContractInfoOf') + } + + /** + * Checks whether the storage item is defined for the current chain version. + */ + get isExists(): boolean { + return this._chain.getStorageItemTypeHash('Contracts', 'ContractInfoOf') != null + } +} + +export class ContractsOwnerInfoOfStorage { + private readonly _chain: Chain + private readonly blockHash: string + + constructor(ctx: BlockContext) + constructor(ctx: ChainContext, block: Block) + constructor(ctx: BlockContext, block?: Block) { + block = block || ctx.block + this.blockHash = block.hash + this._chain = ctx._chain + } + + /** + * A mapping between an original code hash and its owner information. + */ + get isWatrNodeV1000() { + return this._chain.getStorageItemTypeHash('Contracts', 'OwnerInfoOf') === '76689686c73821ee740f33d092a38a05de83a2833f6c8857baa886203c5bf939' + } + + /** + * A mapping between an original code hash and its owner information. + */ + async getAsWatrNodeV1000(key: Uint8Array): Promise<watrNodeV1000.OwnerInfo | undefined> { + assert(this.isWatrNodeV1000) + return this._chain.getStorage(this.blockHash, 'Contracts', 'OwnerInfoOf', key) + } + + async getManyAsWatrNodeV1000(keys: Uint8Array[]): Promise<(watrNodeV1000.OwnerInfo | undefined)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'OwnerInfoOf', keys.map(k => [k])) + } + + async getAllAsWatrNodeV1000(): Promise<(watrNodeV1000.OwnerInfo)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'Contracts', 'OwnerInfoOf') + } + + /** + * Checks whether the storage item is defined for the current chain version. + */ + get isExists(): boolean { + return this._chain.getStorageItemTypeHash('Contracts', 'OwnerInfoOf') != null + } +} + +export class SystemAccountStorage { + private readonly _chain: Chain + private readonly blockHash: string + + constructor(ctx: BlockContext) + constructor(ctx: ChainContext, block: Block) + constructor(ctx: BlockContext, block?: Block) { + block = block || ctx.block + this.blockHash = block.hash + this._chain = ctx._chain + } + + /** + * The full account information for a particular account ID. + */ + get isWatrNodeV1000() { + return this._chain.getStorageItemTypeHash('System', 'Account') === '1ddc7ade926221442c388ee4405a71c9428e548fab037445aaf4b3a78f4735c1' + } + + /** + * The full account information for a particular account ID. + */ + async getAsWatrNodeV1000(key: Uint8Array): Promise<watrNodeV1000.AccountInfo> { + assert(this.isWatrNodeV1000) + return this._chain.getStorage(this.blockHash, 'System', 'Account', key) + } + + async getManyAsWatrNodeV1000(keys: Uint8Array[]): Promise<(watrNodeV1000.AccountInfo)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'System', 'Account', keys.map(k => [k])) + } + + async getAllAsWatrNodeV1000(): Promise<(watrNodeV1000.AccountInfo)[]> { + assert(this.isWatrNodeV1000) + return this._chain.queryStorage(this.blockHash, 'System', 'Account') + } + + /** + * Checks whether the storage item is defined for the current chain version. + */ + get isExists(): boolean { + return this._chain.getStorageItemTypeHash('System', 'Account') != null + } +} diff --git a/squid-ink/src/chains/watrtestnet/types/support.ts b/squid-ink/src/chains/watrtestnet/types/support.ts new file mode 100644 index 0000000..6d150b2 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/types/support.ts @@ -0,0 +1,66 @@ + +export type Result<T, E> = { + __kind: 'Ok' + value: T +} | { + __kind: 'Err' + value: E +} + + +export type Option<T> = { + __kind: 'Some', + value: T +} | { + __kind: 'None' +} + + +export interface Chain { + getEventHash(eventName: string): string + decodeEvent(event: Event): any + getCallHash(name: string): string + decodeCall(call: Call): any + getStorageItemTypeHash(prefix: string, name: string): string | undefined + getStorage(blockHash: string, prefix: string, name: string, ...args: any[]): Promise<any> + queryStorage(blockHash: string, prefix: string, name: string, ...args: any[]): Promise<any[]> + getConstantTypeHash(pallet: string, name: string): string | undefined + getConstant(pallet: string, name: string): any +} + + +export interface ChainContext { + _chain: Chain +} + + +export interface Event { + name: string + args: any +} + + +export interface EventContext extends ChainContext { + event: Event +} + + +export interface Call { + name: string + args: any +} + + +export interface CallContext extends ChainContext { + call: Call +} + + +export interface BlockContext extends ChainContext { + block: Block +} + + +export interface Block { + hash: string +} diff --git a/squid-ink/src/chains/watrtestnet/types/watrNodeV1000.ts b/squid-ink/src/chains/watrtestnet/types/watrNodeV1000.ts new file mode 100644 index 0000000..3b53893 --- /dev/null +++ b/squid-ink/src/chains/watrtestnet/types/watrNodeV1000.ts @@ -0,0 +1,62 @@ +import type {Result, Option} from './support' + +export type MultiAddress = MultiAddress_Id | MultiAddress_Index | MultiAddress_Raw | MultiAddress_Address32 | MultiAddress_Address20 + +export interface MultiAddress_Id { + __kind: 'Id' + value: Uint8Array +} + +export interface MultiAddress_Index { + __kind: 'Index' + value: null +} + +export interface MultiAddress_Raw { + __kind: 'Raw' + value: Uint8Array +} + +export interface MultiAddress_Address32 { + __kind: 'Address32' + value: Uint8Array +} + +export interface MultiAddress_Address20 { + __kind: 'Address20' + value: Uint8Array +} + +export interface AccountData { + free: bigint + reserved: bigint + miscFrozen: bigint + feeFrozen: bigint +} + +export interface PrefabWasmModule { + instructionWeightsVersion: number + initial: number + maximum: number + code: Uint8Array +} + +export interface RawContractInfo { + trieId: Uint8Array + codeHash: Uint8Array + storageDeposit: bigint +} + +export interface OwnerInfo { + owner: Uint8Array + deposit: bigint + refcount: bigint +} + +export interface AccountInfo { + nonce: number + consumers: number + providers: number + sufficients: number + data: AccountData +}