Adjust contract call result decoding (#3510)
* Adjust types for node/node-template * Adjust contract call result * Adjust * Raw directly * CHANGELOG
This commit is contained in:
@@ -12,6 +12,9 @@ Changes:
|
|||||||
- Allow for `Bounded*` type usage (as per latest Substrate)
|
- Allow for `Bounded*` type usage (as per latest Substrate)
|
||||||
- Adjust internal TS definitions for providers
|
- Adjust internal TS definitions for providers
|
||||||
- Fix `ContractRx` query result definitions (no extra Observable)
|
- Fix `ContractRx` query result definitions (no extra Observable)
|
||||||
|
- Handle contract call RPC result directly as `Raw` (returned as hex)
|
||||||
|
- Use `BN_*` constants as non-specified defaults in contract interfaces
|
||||||
|
- Remove all type overrides for node/node-template (API tracks latest)
|
||||||
|
|
||||||
|
|
||||||
## 4.9.1 May 6, 2021
|
## 4.9.1 May 6, 2021
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
|
import type { SubmittableExtrinsic } from '@polkadot/api/submittable/types';
|
||||||
import type { ApiTypes, DecorateMethod } from '@polkadot/api/types';
|
import type { ApiTypes, DecorateMethod } from '@polkadot/api/types';
|
||||||
import type { Bytes } from '@polkadot/types';
|
import type { Bytes } from '@polkadot/types';
|
||||||
import type { AccountId, ContractExecResult, EventRecord, Weight } from '@polkadot/types/interfaces';
|
import type { AccountId, EventRecord, Weight } from '@polkadot/types/interfaces';
|
||||||
import type { AnyJson, CodecArg, ISubmittableResult, Registry } from '@polkadot/types/types';
|
import type { AnyJson, CodecArg, ISubmittableResult } from '@polkadot/types/types';
|
||||||
import type { AbiMessage, ContractCallOutcome, ContractOptions, DecodedEvent } from '../types';
|
import type { AbiMessage, ContractCallOutcome, ContractOptions, DecodedEvent } from '../types';
|
||||||
import type { ContractCallResult, ContractCallSend, ContractQuery, ContractTx, MapMessageQuery, MapMessageTx } from './types';
|
import type { ContractCallResult, ContractCallSend, ContractQuery, ContractTx, MapMessageQuery, MapMessageTx } from './types';
|
||||||
|
|
||||||
@@ -13,11 +13,12 @@ import BN from 'bn.js';
|
|||||||
|
|
||||||
import { SubmittableResult } from '@polkadot/api';
|
import { SubmittableResult } from '@polkadot/api';
|
||||||
import { ApiBase } from '@polkadot/api/base';
|
import { ApiBase } from '@polkadot/api/base';
|
||||||
|
import { createTypeUnsafe } from '@polkadot/types';
|
||||||
import { assert, BN_HUNDRED, BN_ONE, BN_ZERO, bnToBn, isFunction, isUndefined, logger } from '@polkadot/util';
|
import { assert, BN_HUNDRED, BN_ONE, BN_ZERO, bnToBn, isFunction, isUndefined, logger } from '@polkadot/util';
|
||||||
import { map } from '@polkadot/x-rxjs/operators';
|
import { map } from '@polkadot/x-rxjs/operators';
|
||||||
|
|
||||||
import { Abi } from '../Abi';
|
import { Abi } from '../Abi';
|
||||||
import { applyOnEvent, extractOptions, formatData, isOptions } from '../util';
|
import { applyOnEvent, extractOptions, isOptions } from '../util';
|
||||||
import { Base } from './Base';
|
import { Base } from './Base';
|
||||||
|
|
||||||
// As per Rust, 5 * GAS_PER_SEC
|
// As per Rust, 5 * GAS_PER_SEC
|
||||||
@@ -50,33 +51,6 @@ export class ContractSubmittableResult extends SubmittableResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// map from a JSON result to current-style ContractExecResult
|
|
||||||
function mapExecResult (registry: Registry, json: Record<string, AnyJson>): ContractExecResult {
|
|
||||||
if (!Object.keys(json).some((key) => ['error', 'success'].includes(key))) {
|
|
||||||
return registry.createType('ContractExecResult', json);
|
|
||||||
}
|
|
||||||
|
|
||||||
const from = registry.createType('ContractExecResultTo260', json);
|
|
||||||
|
|
||||||
if (from.isSuccess) {
|
|
||||||
const s = from.asSuccess;
|
|
||||||
|
|
||||||
return registry.createType('ContractExecResult', {
|
|
||||||
gasConsumed: s.gasConsumed,
|
|
||||||
result: {
|
|
||||||
ok: {
|
|
||||||
data: s.data,
|
|
||||||
flags: s.flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// in the old format error has no additional information,
|
|
||||||
// map it as-is with an "unknown" error
|
|
||||||
return registry.createType('ContractExecResult', { result: { err: { other: 'unknown' } } });
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
||||||
/**
|
/**
|
||||||
* @description The on-chain address for this contract
|
* @description The on-chain address for this contract
|
||||||
@@ -159,24 +133,24 @@ export class Contract<ApiType extends ApiTypes> extends Base<ApiType> {
|
|||||||
return {
|
return {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
send: this._decorateMethod((origin: string | AccountId | Uint8Array) =>
|
send: this._decorateMethod((origin: string | AccountId | Uint8Array) =>
|
||||||
this.api.rx.rpc.contracts.call.json({
|
this.api.rx.rpc.contracts
|
||||||
dest: this.address,
|
.call({
|
||||||
gasLimit: this.#getGas(gasLimit, true),
|
dest: this.address,
|
||||||
inputData: message.toU8a(params),
|
gasLimit: this.#getGas(gasLimit, true),
|
||||||
origin,
|
inputData: message.toU8a(params),
|
||||||
value
|
origin,
|
||||||
}).pipe(map((json): ContractCallOutcome => {
|
value
|
||||||
const { debugMessage, gasConsumed, result } = mapExecResult(this.registry, json.toJSON());
|
})
|
||||||
|
.pipe(
|
||||||
return {
|
map(({ debugMessage, gasConsumed, result }): ContractCallOutcome => ({
|
||||||
debugMessage,
|
debugMessage,
|
||||||
gasConsumed,
|
gasConsumed,
|
||||||
output: result.isOk && message.returnType
|
output: result.isOk && message.returnType
|
||||||
? formatData(this.registry, result.asOk.data, message.returnType)
|
? createTypeUnsafe(this.registry, message.returnType.type, [result.asOk.data], { isPedantic: true })
|
||||||
: null,
|
: null,
|
||||||
result
|
result
|
||||||
};
|
}))
|
||||||
}))
|
)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,24 +2,18 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import type { SubmittableResult } from '@polkadot/api';
|
import type { SubmittableResult } from '@polkadot/api';
|
||||||
import type { Raw } from '@polkadot/types';
|
|
||||||
import type { EventRecord } from '@polkadot/types/interfaces';
|
import type { EventRecord } from '@polkadot/types/interfaces';
|
||||||
import type { Codec, CodecArg, Registry, TypeDef } from '@polkadot/types/types';
|
import type { CodecArg } from '@polkadot/types/types';
|
||||||
import type { BlueprintOptions, ContractOptions } from './types';
|
import type { BlueprintOptions, ContractOptions } from './types';
|
||||||
|
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
|
||||||
import { createTypeUnsafe } from '@polkadot/types/create';
|
|
||||||
import { isBigInt, isBn, isNumber, isString } from '@polkadot/util';
|
import { isBigInt, isBn, isNumber, isString } from '@polkadot/util';
|
||||||
|
|
||||||
type ContractEvents = 'CodeStored' | 'ContractEmitted' | 'ContractExecution' | 'Instantiated';
|
type ContractEvents = 'CodeStored' | 'ContractEmitted' | 'ContractExecution' | 'Instantiated';
|
||||||
|
|
||||||
type TOptions = BlueprintOptions | ContractOptions;
|
type TOptions = BlueprintOptions | ContractOptions;
|
||||||
|
|
||||||
export function formatData (registry: Registry, data: Raw, { type }: TypeDef): Codec {
|
|
||||||
return createTypeUnsafe(registry, type, [data], { isPedantic: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applyOnEvent <T> (result: SubmittableResult, types: ContractEvents[], fn: (records: EventRecord[]) => T): T | undefined {
|
export function applyOnEvent <T> (result: SubmittableResult, types: ContractEvents[], fn: (records: EventRecord[]) => T): T | undefined {
|
||||||
if (result.isInBlock || result.isFinalized) {
|
if (result.isInBlock || result.isFinalized) {
|
||||||
const records = result.filterRecords('contracts', types);
|
const records = result.filterRecords('contracts', types);
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ export default {
|
|||||||
},
|
},
|
||||||
ContractExecResultOk: {
|
ContractExecResultOk: {
|
||||||
flags: 'u32',
|
flags: 'u32',
|
||||||
data: 'Bytes'
|
// only via RPC, decode hex exactly as received
|
||||||
|
data: 'Raw'
|
||||||
},
|
},
|
||||||
ContractExecResultResult: {
|
ContractExecResultResult: {
|
||||||
_enum: {
|
_enum: {
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export interface ContractExecResultErrModule extends Struct {
|
|||||||
/** @name ContractExecResultOk */
|
/** @name ContractExecResultOk */
|
||||||
export interface ContractExecResultOk extends Struct {
|
export interface ContractExecResultOk extends Struct {
|
||||||
readonly flags: u32;
|
readonly flags: u32;
|
||||||
readonly data: Bytes;
|
readonly data: Raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @name ContractExecResultResult */
|
/** @name ContractExecResultResult */
|
||||||
|
|||||||
Reference in New Issue
Block a user