From 8adcdda125141ceb1f4747c5936b141d6a0e0f45 Mon Sep 17 00:00:00 2001 From: ac10n Date: Thu, 2 May 2024 12:48:15 -0700 Subject: [PATCH] Using static variable to configure field numbering strategy (#2939) * feat: option for the user to choose between legacy and updated field numbering * chore: fix typo * chore: add typedoc comments for Token.fieldNumberingStrategy * test: fix failing integration test --- ...ations-call-by-index-methodsObject.spec.ts | 97 ++-- .../src/taquito-michelson-encoder.ts | 2 +- .../src/tokens/or.ts | 53 ++- .../src/tokens/pair.ts | 15 +- .../src/tokens/token.ts | 24 + .../test/sample1.spec.ts | 227 ++++++++- .../test/sample11_dexter.spec.ts | 372 ++++++++++++++- .../test/sample9_lambda.spec.ts | 117 ++++- .../test/tokens/or.spec.ts | 429 +++++++++++++++--- packages/taquito/src/taquito.ts | 11 +- 10 files changed, 1179 insertions(+), 168 deletions(-) diff --git a/integration-tests/__tests__/contract/no-annotations-call-by-index-methodsObject.spec.ts b/integration-tests/__tests__/contract/no-annotations-call-by-index-methodsObject.spec.ts index 946ec4354b..8287f37f00 100644 --- a/integration-tests/__tests__/contract/no-annotations-call-by-index-methodsObject.spec.ts +++ b/integration-tests/__tests__/contract/no-annotations-call-by-index-methodsObject.spec.ts @@ -1,3 +1,4 @@ +import { FieldNumberingStrategy } from "@taquito/michelson-encoder"; import { CONFIGS } from "../../config"; import { noAnnotCode, noAnnotInit } from "../../data/token_without_annotation"; @@ -10,59 +11,67 @@ CONFIGS().forEach(({ lib, rpc, setup }) => { beforeEach(async () => { await setup() }) - it('Verify contract.originate for a contract with no annotations for methods using methodObjects', async () => { - // Constants to replace annotations - const ACCOUNTS = '0'; - const BALANCE = '0'; - const ALLOWANCES = '1'; - const TRANSFER = '0'; - const APPROVE = '2'; - // Actual tests + // Constants to replace annotations + const ACCOUNTS = '0'; + const BALANCE = '0'; + const ALLOWANCES = '1'; + const TRANSFER = '0'; + const APPROVE = '2'; - const ACCOUNT1_ADDRESS = await Tezos.signer.publicKeyHash() - const ACCOUNT2_ADDRESS = 'tz1ZfrERcALBwmAqwonRXYVQBDT9BjNjBHJu' + // Actual tests - // Originate a contract with a known state - const op = await Tezos.contract.originate({ - balance: "1", - code: noAnnotCode, - init: noAnnotInit(await Tezos.signer.publicKeyHash()) - }) - await op.confirmation() - const contract = await op.contract() + const ACCOUNT2_ADDRESS = 'tz1ZfrERcALBwmAqwonRXYVQBDT9BjNjBHJu' + + const testContract = (strategy: FieldNumberingStrategy, innerObjectStartingIndex: number) => { + it(`Verify contract.originate for a contract with no annotations for methods using methodObjects with fieldNumberingStrategy: ${strategy}`, async () => { + Tezos.setFieldNumberingStrategy(strategy); + const ACCOUNT1_ADDRESS = await Tezos.signer.publicKeyHash() + // Originate a contract with a known state + const op = await Tezos.contract.originate({ + balance: "1", + code: noAnnotCode, + init: noAnnotInit(await Tezos.signer.publicKeyHash()) + }) + await op.confirmation() + const contract = await op.contract() - // Make a transfer + // Make a transfer - const operation = await contract.methodsObject[TRANSFER]({ - 0: ACCOUNT1_ADDRESS, - 1: ACCOUNT2_ADDRESS, - 2: "1" - }).send(); + const operation = await contract.methodsObject[TRANSFER]({ + 0: ACCOUNT1_ADDRESS, + 1: ACCOUNT2_ADDRESS, + 2: "1" + }).send(); - await operation.confirmation(); - expect(operation.status).toEqual('applied') + await operation.confirmation(); + expect(operation.status).toEqual('applied') - // Verify that the transfer was done as expected - const storage = await contract.storage() - let account1 = await storage[ACCOUNTS].get(ACCOUNT1_ADDRESS) - expect(account1[BALANCE].toString()).toEqual('16') + // Verify that the transfer was done as expected + const storage = await contract.storage() + let account1 = await storage[ACCOUNTS].get(ACCOUNT1_ADDRESS) + expect(account1[BALANCE].toString()).toEqual('16') - const account2 = await storage[ACCOUNTS].get(ACCOUNT2_ADDRESS) - expect(account2[BALANCE].toString()).toEqual('1') + const account2 = await storage[ACCOUNTS].get(ACCOUNT2_ADDRESS) + expect(account2[BALANCE].toString()).toEqual('1') - // Approve - const operation2 = await contract.methodsObject[APPROVE]({ - 0: ACCOUNT2_ADDRESS, - 1: "1" - }).send(); + // Approve + const operation2 = await contract.methodsObject[APPROVE]({ + [innerObjectStartingIndex]: ACCOUNT2_ADDRESS, + [innerObjectStartingIndex + 1]: "1" + }).send(); - await operation2.confirmation(); - expect(operation2.status).toEqual('applied') + await operation2.confirmation(); + expect(operation2.status).toEqual('applied') + + // Verify that the allowance was done as expected + account1 = await storage[ACCOUNTS].get(ACCOUNT1_ADDRESS) + expect(account1[ALLOWANCES].get(ACCOUNT2_ADDRESS).toString()).toEqual('1') + }); + }; + testContract('Legacy', 2); + testContract('ResetFieldNumbersInNestedObjects', 0); + testContract('Latest', 0); - // Verify that the allowance was done as expected - account1 = await storage[ACCOUNTS].get(ACCOUNT1_ADDRESS) - expect(account1[ALLOWANCES].get(ACCOUNT2_ADDRESS).toString()).toEqual('1') - }) }); -}) +}); diff --git a/packages/taquito-michelson-encoder/src/taquito-michelson-encoder.ts b/packages/taquito-michelson-encoder/src/taquito-michelson-encoder.ts index eca1e3d16f..92fead1cfb 100644 --- a/packages/taquito-michelson-encoder/src/taquito-michelson-encoder.ts +++ b/packages/taquito-michelson-encoder/src/taquito-michelson-encoder.ts @@ -16,4 +16,4 @@ export const UnitValue = Symbol(); export const SaplingStateValue = {}; export * from './michelson-map'; export { VERSION } from './version'; -export { Token } from './tokens/token'; +export { FieldNumberingStrategy, Token } from './tokens/token'; diff --git a/packages/taquito-michelson-encoder/src/tokens/or.ts b/packages/taquito-michelson-encoder/src/tokens/or.ts index f33c165525..8b748e5b8c 100644 --- a/packages/taquito-michelson-encoder/src/tokens/or.ts +++ b/packages/taquito-michelson-encoder/src/tokens/or.ts @@ -38,13 +38,17 @@ export class OrToken extends ComparableToken { public Encode(args: any[]): any { const label = args[args.length - 1]; - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); if (String(leftToken.annot()) === String(label) && !(leftToken instanceof OrToken)) { args.pop(); @@ -71,13 +75,17 @@ export class OrToken extends ComparableToken { } public ExtractSignature(): any { - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); const newSig = []; @@ -107,13 +115,17 @@ export class OrToken extends ComparableToken { this.validateJavascriptObject(args); const label = Object.keys(args)[0]; - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); if (String(leftToken.annot()) === String(label) && !(leftToken instanceof OrToken)) { return { prim: 'Left', args: [leftToken.EncodeObject(args[label], semantic)] }; @@ -159,12 +171,16 @@ export class OrToken extends ComparableToken { * @throws {@link OrValidationError} */ public Execute(val: any, semantics?: Semantic): any { - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); if (val.prim === 'Right') { if (rightToken instanceof OrToken) { @@ -195,7 +211,7 @@ export class OrToken extends ComparableToken { getRightValue: (token: Token) => any, concat: (left: any, right: any) => any ) { - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; let leftValue; if (leftToken instanceof OrToken) { @@ -205,7 +221,11 @@ export class OrToken extends ComparableToken { leftValue = { [leftToken.annot()]: getLeftValue(leftToken) }; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); let rightValue; if (rightToken instanceof OrToken) { rightValue = getRightValue(rightToken); @@ -260,13 +280,17 @@ export class OrToken extends ComparableToken { } private findToken(label: any): Token | null { - const leftToken = this.createToken(this.val.args[0], this.getIdx(), 'Or'); + const leftToken = this.createToken(this.val.args[0], this.getIdxForChildren(), 'Or'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(this.val.args[1], this.getIdx() + keyCount, 'Or'); + const rightToken = this.createToken( + this.val.args[1], + this.getIdxForChildren() + keyCount, + 'Or' + ); if ( String(leftToken.annot()) === String(label) && @@ -340,7 +364,10 @@ export class OrToken extends ComparableToken { return tokens; } - protected getIdx(): number { + protected getIdxForChildren(): number { + if (Token.fieldNumberingStrategy === 'Legacy') { + return this.idx; + } return this.parentTokenType === 'Or' ? this.idx : 0; } } diff --git a/packages/taquito-michelson-encoder/src/tokens/pair.ts b/packages/taquito-michelson-encoder/src/tokens/pair.ts index 138c7f5323..7d73aaa166 100644 --- a/packages/taquito-michelson-encoder/src/tokens/pair.ts +++ b/packages/taquito-michelson-encoder/src/tokens/pair.ts @@ -97,7 +97,7 @@ export class PairToken extends ComparableToken { private tokens(): [Token, Token] { let cnt = 0; return this.args().map((a) => { - const tok = this.createToken(a, this.getIdx() + cnt, 'Pair'); + const tok = this.createToken(a, this.getIdxForChildren() + cnt, 'Pair'); if (tok instanceof PairToken) { cnt += Object.keys(tok.ExtractSchema()).length; } else { @@ -116,13 +116,13 @@ export class PairToken extends ComparableToken { public ExtractSignature(): any { const args = this.args(); - const leftToken = this.createToken(args[0], this.getIdx(), 'Pair'); + const leftToken = this.createToken(args[0], this.getIdxForChildren(), 'Pair'); let keyCount = 1; if (leftToken instanceof OrToken) { keyCount = Object.keys(leftToken.ExtractSchema()).length; } - const rightToken = this.createToken(args[1], this.getIdx() + keyCount, 'Pair'); + const rightToken = this.createToken(args[1], this.getIdxForChildren() + keyCount, 'Pair'); const newSig = []; @@ -175,7 +175,7 @@ export class PairToken extends ComparableToken { private traversal(getLeftValue: (token: Token) => any, getRightValue: (token: Token) => any) { const args = this.args(); - const leftToken = this.createToken(args[0], this.getIdx(), 'Pair'); + const leftToken = this.createToken(args[0], this.getIdxForChildren(), 'Pair'); let keyCount = 1; let leftValue; if (leftToken instanceof PairToken && !leftToken.hasAnnotations()) { @@ -187,7 +187,7 @@ export class PairToken extends ComparableToken { leftValue = { [leftToken.annot()]: getLeftValue(leftToken) }; } - const rightToken = this.createToken(args[1], this.getIdx() + keyCount, 'Pair'); + const rightToken = this.createToken(args[1], this.getIdxForChildren() + keyCount, 'Pair'); let rightValue; if (rightToken instanceof PairToken && !rightToken.hasAnnotations()) { rightValue = getRightValue(rightToken); @@ -282,7 +282,10 @@ export class PairToken extends ComparableToken { return tokens; } - protected getIdx(): number { + protected getIdxForChildren(): number { + if (Token.fieldNumberingStrategy === 'Legacy') { + return this.idx; + } return this.parentTokenType === 'Pair' ? this.idx : 0; } } diff --git a/packages/taquito-michelson-encoder/src/tokens/token.ts b/packages/taquito-michelson-encoder/src/tokens/token.ts index 63b898cc5e..de71d7ba58 100644 --- a/packages/taquito-michelson-encoder/src/tokens/token.ts +++ b/packages/taquito-michelson-encoder/src/tokens/token.ts @@ -35,7 +35,31 @@ export interface SemanticEncoding { [key: string]: (value: any, type?: MichelsonV1Expression) => MichelsonV1Expression; } +/** + * @description Possible strategies for mapping between javascript classes and Michelson values + * Legacy: The old behaviour: { annot1: 'some value', annot2: 'other Value', annot3: { 2: 'yet another value', 3: 'also some value' }} + * ResetFieldNumbersInNestedObjects: { annot1: 'some value', annot2: 'other Value', annot3: { 0: 'yet another value', 1: 'also some value' }} + * Latest: This will include new changes as we might implement in the future. This is the suggested value if it does not break your code + */ +export type FieldNumberingStrategy = 'Legacy' | 'ResetFieldNumbersInNestedObjects' | 'Latest'; + export abstract class Token { + private static _fieldNumberingStrategy: FieldNumberingStrategy = 'Latest'; + + /** + * @description Gets the strategy used for field numbering in Token execute/encode/decode to convert Michelson values to/from javascript objects, returns a value of type {@link FieldNumberingStrategy} that controls how field numbers are calculated + */ + static get fieldNumberingStrategy() { + return Token._fieldNumberingStrategy; + } + + /** + * @description Sets the strategy used for field numbering in Token execute/encode/decode to convert Michelson values to/from javascript objects, accepts a value of type {@link FieldNumberingStrategy} that controls how field numbers are calculated + */ + static set fieldNumberingStrategy(value: FieldNumberingStrategy) { + Token._fieldNumberingStrategy = value; + } + constructor( protected val: MichelsonV1ExpressionExtended, protected idx: number, diff --git a/packages/taquito-michelson-encoder/test/sample1.spec.ts b/packages/taquito-michelson-encoder/test/sample1.spec.ts index c6a554b6d4..415fa11f03 100644 --- a/packages/taquito-michelson-encoder/test/sample1.spec.ts +++ b/packages/taquito-michelson-encoder/test/sample1.spec.ts @@ -4,6 +4,7 @@ import { ParameterSchema } from '../src/schema/parameter'; import { Schema } from '../src/schema/storage'; import { MichelsonMap } from '../src/michelson-map'; import { expectMichelsonMap } from './utils'; +import { Token } from '../src/taquito-michelson-encoder'; describe('Schema test', () => { it('Should extract schema properly', () => { @@ -202,8 +203,41 @@ describe('Schema test', () => { it('Should build parameter schema properly', () => { const schema = new ParameterSchema(params); - const s = schema.ExtractSchema(); - expect(s).toEqual({ + const extractSchema_Legacy = { + allowance: { + '4': 'address', + '5': 'address', + NatNatContract: 'contract', + }, + approve: { + '1': 'address', + '2': 'nat', + }, + balanceOf: { + '3': 'address', + NatContract: 'contract', + }, + createAccount: { + '5': 'address', + '6': 'nat', + }, + createAccounts: { + list: { + '6': 'address', + '7': 'nat', + }, + }, + transfer: { + '0': 'address', + '1': 'nat', + }, + transferFrom: { + '2': 'address', + '3': 'address', + '4': 'nat', + }, + }; + const extractSchema_ResetFields = { allowance: { '0': 'address', '1': 'address', @@ -236,9 +270,136 @@ describe('Schema test', () => { '1': 'address', '2': 'nat', }, - }); + }; - expect(schema.generateSchema()).toEqual({ + const generateSchema_Legacy = { + __michelsonType: 'or', + schema: { + allowance: { + __michelsonType: 'pair', + schema: { + '4': { + __michelsonType: 'address', + schema: 'address', + }, + '5': { + __michelsonType: 'address', + schema: 'address', + }, + NatNatContract: { + __michelsonType: 'contract', + schema: { + parameter: { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'nat', + schema: 'nat', + }, + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + }, + }, + }, + }, + approve: { + __michelsonType: 'pair', + schema: { + '1': { + __michelsonType: 'address', + schema: 'address', + }, + '2': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + balanceOf: { + __michelsonType: 'pair', + schema: { + '3': { + __michelsonType: 'address', + schema: 'address', + }, + NatContract: { + __michelsonType: 'contract', + schema: { + parameter: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + }, + }, + createAccount: { + __michelsonType: 'pair', + schema: { + '5': { + __michelsonType: 'address', + schema: 'address', + }, + '6': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + createAccounts: { + __michelsonType: 'list', + schema: { + __michelsonType: 'pair', + schema: { + '6': { + __michelsonType: 'address', + schema: 'address', + }, + '7': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + }, + transfer: { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'address', + schema: 'address', + }, + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + transferFrom: { + __michelsonType: 'pair', + schema: { + '2': { + __michelsonType: 'address', + schema: 'address', + }, + '3': { + __michelsonType: 'address', + schema: 'address', + }, + '4': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + }, + }; + + const generateSchema_ResetFields = { __michelsonType: 'or', schema: { allowance: { @@ -363,7 +524,17 @@ describe('Schema test', () => { }, }, }, - }); + }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.ExtractSchema()).toEqual(extractSchema_Legacy); + expect(schema.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); }); it('Should extract signature properly', () => { @@ -373,14 +544,29 @@ describe('Schema test', () => { expect(sig).toContainEqual(['approve', 'address', 'nat']); expect(sig).toContainEqual(['balanceOf', 'address', 'contract']); expect(sig).toContainEqual(['createAccount', 'address', 'nat']); - expect(sig).toContainEqual([ - 'createAccounts', - { - list: { - '0': 'address', - '1': 'nat', - }, + const createAccount_Legacy = { + list: { + '6': 'address', + '7': 'nat', }, + }; + const createAccount_ResetFields = { + list: { + '0': 'address', + '1': 'nat', + }, + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.ExtractSignatures()).toContainEqual(['createAccounts', createAccount_Legacy]); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.ExtractSignatures()).toContainEqual([ + 'createAccounts', + createAccount_ResetFields, + ]); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.ExtractSignatures()).toContainEqual([ + 'createAccounts', + createAccount_ResetFields, ]); expect(sig).toContainEqual(['transfer', 'address', 'nat']); expect(sig).toContainEqual(['transferFrom', 'address', 'address', 'nat']); @@ -388,13 +574,24 @@ describe('Schema test', () => { it('Should parse parameter properly', () => { const schema = new ParameterSchema(params); - const s = schema.Execute(txParams); - expect(s).toEqual({ + const execute_Legacy = { + approve: { + '1': 'tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD', + '2': new BigNumber('60'), + }, + }; + const execute_ResetFields = { approve: { '0': 'tz1fPjyo55HwUAkd1xcL5vo6DGzJrkxAMpiD', '1': new BigNumber('60'), }, - }); + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.Execute(txParams)).toEqual(execute_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.Execute(txParams)).toEqual(execute_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.Execute(txParams)).toEqual(execute_ResetFields); }); it(`Should find the value that corresponds to the type ({ prim: 'string', annots: ['%name'] }) in top-level pairs of the storage`, () => { diff --git a/packages/taquito-michelson-encoder/test/sample11_dexter.spec.ts b/packages/taquito-michelson-encoder/test/sample11_dexter.spec.ts index 93acfa707d..f0f90c0c49 100644 --- a/packages/taquito-michelson-encoder/test/sample11_dexter.spec.ts +++ b/packages/taquito-michelson-encoder/test/sample11_dexter.spec.ts @@ -9,10 +9,49 @@ import BigNumber from 'bignumber.js'; import { ParameterSchema } from '../src/schema/parameter'; import { MichelsonMap } from '../src/michelson-map'; import { expectMichelsonMap } from './utils'; +import { Token } from '../src/taquito-michelson-encoder'; describe('Exchange contract test', () => { it('Test storage schema', () => { const schema = new Schema(storageDexter); - expect(schema.ExtractSchema()).toEqual({ + const extractSchema_Legacy = { + '0': { + big_map: { + key: 'address', + value: 'nat', + }, + }, + '1': 'contract', + '2': 'contract', + '3': 'nat', + '4': { + map: { + key: 'address', + value: { + '0': { + '0': 'nat', + '1': 'nat', + '2': 'timestamp', + }, + '1': { + '1': 'nat', + '2': 'mutez', + '3': 'nat', + '4': 'timestamp', + }, + '2': { + '2': 'nat', + '3': 'timestamp', + }, + '3': { + '3': 'nat', + '4': 'mutez', + '5': 'timestamp', + }, + }, + }, + }, + }; + const extractSchema_ResetFields = { '0': { big_map: { key: 'address', @@ -49,9 +88,205 @@ describe('Exchange contract test', () => { }, }, }, - }); + }; - expect(schema.generateSchema()).toEqual({ + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.ExtractSchema()).toEqual(extractSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + + const generateSchema_Legacy = { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'big_map', + schema: { + key: { + __michelsonType: 'address', + schema: 'address', + }, + value: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + '1': { + __michelsonType: 'contract', + schema: { + parameter: { + __michelsonType: 'or', + schema: { + 0: { + __michelsonType: 'pair', + schema: { + 0: { + __michelsonType: 'address', + schema: 'address', + }, + 1: { + __michelsonType: 'contract', + schema: { + parameter: { + __michelsonType: 'or', + schema: { + 0: { + __michelsonType: 'pair', + schema: { + 0: { + __michelsonType: 'address', + schema: 'address', + }, + 1: { + __michelsonType: 'address', + schema: 'address', + }, + 2: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + 1: { + __michelsonType: 'address', + schema: 'address', + }, + }, + }, + }, + }, + }, + }, + 1: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + }, + }, + '2': { + __michelsonType: 'contract', + schema: { + parameter: { + __michelsonType: 'or', + schema: { + 0: { + __michelsonType: 'pair', + schema: { + 0: { + __michelsonType: 'address', + schema: 'address', + }, + 1: { + __michelsonType: 'address', + schema: 'address', + }, + 2: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }, + 1: { + __michelsonType: 'address', + schema: 'address', + }, + }, + }, + }, + }, + '3': { + __michelsonType: 'nat', + schema: 'nat', + }, + '4': { + __michelsonType: 'map', + schema: { + key: { + __michelsonType: 'address', + schema: 'address', + }, + value: { + __michelsonType: 'or', + schema: { + '0': { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'nat', + schema: 'nat', + }, + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + '2': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '1': { + __michelsonType: 'pair', + schema: { + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + '2': { + __michelsonType: 'mutez', + schema: 'mutez', + }, + '3': { + __michelsonType: 'nat', + schema: 'nat', + }, + '4': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '2': { + __michelsonType: 'pair', + schema: { + '2': { + __michelsonType: 'nat', + schema: 'nat', + }, + '3': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '3': { + __michelsonType: 'pair', + schema: { + '3': { + __michelsonType: 'nat', + schema: 'nat', + }, + '4': { + __michelsonType: 'mutez', + schema: 'mutez', + }, + '5': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + }, + }, + }, + }, + }, + }; + const generateSchema_ResetFields = { __michelsonType: 'pair', schema: { '0': { @@ -239,7 +474,14 @@ describe('Exchange contract test', () => { }, }, }, - }); + }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); }); it('Test storage parsing', () => { @@ -271,7 +513,30 @@ describe('Exchange contract test', () => { it('Test parameter schema', () => { const schema = new ParameterSchema(params); - expect(schema.ExtractSchema()).toEqual({ + const extractSchema_Legacy = { + '0': { + '0': 'nat', + '1': 'nat', + '2': 'timestamp', + }, + '1': { + '1': 'nat', + '2': 'mutez', + '3': 'nat', + '4': 'timestamp', + }, + '2': { + '2': 'nat', + '3': 'timestamp', + }, + '3': { + '3': 'nat', + '4': 'mutez', + '5': 'timestamp', + }, + '4': 'nat', + }; + const extractSchema_ResetFields = { '0': { '0': 'nat', '1': 'nat', @@ -293,9 +558,93 @@ describe('Exchange contract test', () => { '2': 'timestamp', }, '4': 'nat', - }); + }; - expect(schema.generateSchema()).toEqual({ + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.ExtractSchema()).toEqual(extractSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + + const generateSchema_Legacy = { + __michelsonType: 'or', + schema: { + '0': { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'nat', + schema: 'nat', + }, + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + '2': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '1': { + __michelsonType: 'pair', + schema: { + '1': { + __michelsonType: 'nat', + schema: 'nat', + }, + '2': { + __michelsonType: 'mutez', + schema: 'mutez', + }, + '3': { + __michelsonType: 'nat', + schema: 'nat', + }, + '4': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '2': { + __michelsonType: 'pair', + schema: { + '2': { + __michelsonType: 'nat', + schema: 'nat', + }, + '3': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '3': { + __michelsonType: 'pair', + schema: { + '3': { + __michelsonType: 'nat', + schema: 'nat', + }, + '4': { + __michelsonType: 'mutez', + schema: 'mutez', + }, + '5': { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + '4': { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }; + const generateSchema_ResetFields = { __michelsonType: 'or', schema: { '0': { @@ -371,7 +720,14 @@ describe('Exchange contract test', () => { schema: 'nat', }, }, - }); + }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); }); it('Encode parameter properly func 0', () => { diff --git a/packages/taquito-michelson-encoder/test/sample9_lambda.spec.ts b/packages/taquito-michelson-encoder/test/sample9_lambda.spec.ts index 7cd50bd866..8b025b4d7c 100644 --- a/packages/taquito-michelson-encoder/test/sample9_lambda.spec.ts +++ b/packages/taquito-michelson-encoder/test/sample9_lambda.spec.ts @@ -2,17 +2,39 @@ import { params as params9 } from '../data/sample9'; import { ParameterSchema } from '../src/schema/parameter'; +import { Token } from '../src/taquito-michelson-encoder'; describe('Schema test', () => { it('Should parse storage properly', () => { const schema = new ParameterSchema(params9); - const storage = schema.ExtractSchema(); expect(schema.ExtractSignatures()).toContainEqual(['0', 'address', 'string', 'bytes']); expect(schema.ExtractSignatures()).toContainEqual(['1', 'mutez']); expect(schema.ExtractSignatures()).toContainEqual(['2', 'address', 'bool']); - expect(storage).toEqual({ + const extractSchema_Legacy = { + '0': { + '0': 'address', + '1': 'string', + '2': { Some: 'bytes' }, + }, + '1': 'mutez', + '2': { + '2': 'address', + '3': 'bool', + }, + '3': { + lambda: { + parameters: { + 5: { Some: 'bytes' }, + 3: 'address', + 4: 'string', + }, + returns: 'operation', + }, + }, + }; + const extractSchema_ResetFields = { '0': { '0': 'address', '1': 'string', @@ -33,9 +55,87 @@ describe('Schema test', () => { returns: 'operation', }, }, - }); + }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.ExtractSchema()).toEqual(extractSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.ExtractSchema()).toEqual(extractSchema_ResetFields); - expect(schema.generateSchema()).toEqual({ + const generateSchema_Legacy = { + __michelsonType: 'or', + schema: { + '0': { + __michelsonType: 'pair', + schema: { + '0': { + __michelsonType: 'address', + schema: 'address', + }, + '1': { + __michelsonType: 'string', + schema: 'string', + }, + '2': { + __michelsonType: 'option', + schema: { + __michelsonType: 'bytes', + schema: 'bytes', + }, + }, + }, + }, + '1': { + __michelsonType: 'mutez', + schema: 'mutez', + }, + '2': { + __michelsonType: 'pair', + schema: { + '2': { + __michelsonType: 'address', + schema: 'address', + }, + '3': { + __michelsonType: 'bool', + schema: 'bool', + }, + }, + }, + '3': { + __michelsonType: 'lambda', + schema: { + parameters: { + __michelsonType: 'pair', + schema: { + 3: { + __michelsonType: 'address', + schema: 'address', + }, + 4: { + __michelsonType: 'string', + schema: 'string', + }, + 5: { + __michelsonType: 'option', + schema: { + __michelsonType: 'bytes', + schema: 'bytes', + }, + }, + }, + }, + returns: { + __michelsonType: 'operation', + schema: 'operation', + }, + }, + }, + }, + }; + const generateSchema_ResetFields = { __michelsonType: 'or', schema: { '0': { @@ -105,7 +205,14 @@ describe('Schema test', () => { }, }, }, - }); + }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.generateSchema()).toEqual(generateSchema_ResetFields); expect({ args: [ diff --git a/packages/taquito-michelson-encoder/test/tokens/or.spec.ts b/packages/taquito-michelson-encoder/test/tokens/or.spec.ts index 9ac252106c..ba784290d4 100644 --- a/packages/taquito-michelson-encoder/test/tokens/or.spec.ts +++ b/packages/taquito-michelson-encoder/test/tokens/or.spec.ts @@ -10,7 +10,7 @@ import { tokenNoAnnots, tokenOrWithOption, } from '../data/or-tokens'; -import { Schema } from '../../src/taquito-michelson-encoder'; +import { Schema, Token } from '../../src/taquito-michelson-encoder'; describe('Or token', () => { describe('generateSchema, EncodeObject, Execute', () => { @@ -152,11 +152,8 @@ describe('Or token', () => { }, ], }); - expect( - tokenComplexNoAnnots.EncodeObject({ - 1: { 0: 3, 1: 4, 2: 31, 3: '2019-09-06T15:08:29.000Z' }, - }) - ).toEqual({ + + let encode_Expected: object = { prim: 'Left', args: [ { @@ -175,8 +172,17 @@ describe('Or token', () => { ], }, ], - }); - expect(tokenComplexNoAnnots.EncodeObject({ 2: { 0: 3, 1: 'test' } })).toEqual({ + }; + let object_Legacy: object = { 1: { 1: 3, 2: 4, 3: 31, 4: '2019-09-06T15:08:29.000Z' } }; + let object_ResetFields: object = { 1: { 0: 3, 1: 4, 2: 31, 3: '2019-09-06T15:08:29.000Z' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplexNoAnnots.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + + encode_Expected = { prim: 'Right', args: [ { @@ -184,12 +190,17 @@ describe('Or token', () => { args: [{ prim: 'Pair', args: [{ int: '3' }, { string: 'test' }] }], }, ], - }); - expect( - tokenComplexNoAnnots.EncodeObject({ - 3: { 0: 4, 1: 3, 2: '2019-09-06T15:08:29.000Z' }, - }) - ).toEqual({ + }; + object_Legacy = { 2: { 2: 3, 3: 'test' } }; + object_ResetFields = { 2: { 0: 3, 1: 'test' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplexNoAnnots.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + + encode_Expected = { prim: 'Right', args: [ { @@ -213,7 +224,15 @@ describe('Or token', () => { ], }, ], - }); + }; + object_Legacy = { 3: { 3: 4, 4: 3, 5: '2019-09-06T15:08:29.000Z' } }; + object_ResetFields = { 3: { 0: 4, 1: 3, 2: '2019-09-06T15:08:29.000Z' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplexNoAnnots.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplexNoAnnots.EncodeObject(object_ResetFields)).toEqual(encode_Expected); expect(tokenComplexNoAnnots.EncodeObject({ 4: 4 })).toEqual({ prim: 'Right', args: [{ prim: 'Right', args: [{ prim: 'Right', args: [{ int: '4' }] }] }], @@ -243,11 +262,8 @@ describe('Or token', () => { }, ], }); - expect( - tokenComplex.EncodeObject({ - option1: { 0: 3, 1: 4, 2: 31, 3: '2019-09-06T15:08:29.000Z' }, - }) - ).toEqual({ + + encode_Expected = { prim: 'Left', args: [ { @@ -266,8 +282,17 @@ describe('Or token', () => { ], }, ], - }); - expect(tokenComplex.EncodeObject({ option2: { 0: 3, 1: 'test' } })).toEqual({ + }; + object_Legacy = { option1: { 1: 3, 2: 4, 3: 31, 4: '2019-09-06T15:08:29.000Z' } }; + object_ResetFields = { option1: { 0: 3, 1: 4, 2: 31, 3: '2019-09-06T15:08:29.000Z' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplex.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + + encode_Expected = { prim: 'Right', args: [ { @@ -275,12 +300,17 @@ describe('Or token', () => { args: [{ prim: 'Pair', args: [{ int: '3' }, { string: 'test' }] }], }, ], - }); - expect( - tokenComplex.EncodeObject({ - option3: { 0: 4, 1: 3, 2: '2019-09-06T15:08:29.000Z' }, - }) - ).toEqual({ + }; + object_Legacy = { option2: { 2: 3, 3: 'test' } }; + object_ResetFields = { option2: { 0: 3, 1: 'test' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplex.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + + encode_Expected = { prim: 'Right', args: [ { @@ -304,7 +334,16 @@ describe('Or token', () => { ], }, ], - }); + }; + object_Legacy = { option3: { 3: 4, 4: 3, 5: '2019-09-06T15:08:29.000Z' } }; + object_ResetFields = { option3: { 0: 4, 1: 3, 2: '2019-09-06T15:08:29.000Z' } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplex.EncodeObject(object_Legacy)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplex.EncodeObject(object_ResetFields)).toEqual(encode_Expected); + expect(tokenComplex.EncodeObject({ option4: 4 })).toEqual({ prim: 'Right', args: [{ prim: 'Right', args: [{ prim: 'Right', args: [{ int: '4' }] }] }], @@ -575,15 +614,105 @@ describe('Or token', () => { }, }); - expect(tokenComplexNoAnnots.ExtractSchema()).toEqual({ + let extractSchema_Legacy: object = { + 0: { 0: 'nat', 1: 'nat', 2: 'timestamp' }, + 1: { 1: 'nat', 2: 'mutez', 3: 'nat', 4: 'timestamp' }, + 2: { 2: 'nat', 3: 'timestamp' }, + 3: { 3: 'nat', 4: 'mutez', 5: 'timestamp' }, + 4: 'nat', + }; + let extractSchema_ResetFields: object = { 0: { 0: 'nat', 1: 'nat', 2: 'timestamp' }, 1: { 0: 'nat', 1: 'mutez', 2: 'nat', 3: 'timestamp' }, 2: { 0: 'nat', 1: 'timestamp' }, 3: { 0: 'nat', 1: 'mutez', 2: 'timestamp' }, 4: 'nat', - }); + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplexNoAnnots.ExtractSchema()).toEqual(extractSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplexNoAnnots.ExtractSchema()).toEqual(extractSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplexNoAnnots.ExtractSchema()).toEqual(extractSchema_ResetFields); - expect(tokenComplexNoAnnots.generateSchema()).toEqual({ + let generateSchema_Legacy: object = { + __michelsonType: 'or', + schema: { + 0: { + __michelsonType: 'pair', + schema: { + 0: { + __michelsonType: 'nat', + schema: 'nat', + }, + 1: { + __michelsonType: 'nat', + schema: 'nat', + }, + 2: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + 1: { + __michelsonType: 'pair', + schema: { + 1: { + __michelsonType: 'nat', + schema: 'nat', + }, + 2: { + __michelsonType: 'mutez', + schema: 'mutez', + }, + 3: { + __michelsonType: 'nat', + schema: 'nat', + }, + 4: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + 2: { + __michelsonType: 'pair', + schema: { + 2: { + __michelsonType: 'nat', + schema: 'nat', + }, + 3: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + 3: { + __michelsonType: 'pair', + schema: { + 3: { + __michelsonType: 'nat', + schema: 'nat', + }, + 4: { + __michelsonType: 'mutez', + schema: 'mutez', + }, + 5: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + 4: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }; + let generateSchema_ResetFields: object = { __michelsonType: 'or', schema: { 0: { @@ -659,17 +788,113 @@ describe('Or token', () => { schema: 'nat', }, }, - }); + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplexNoAnnots.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplexNoAnnots.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplexNoAnnots.generateSchema()).toEqual(generateSchema_ResetFields); - expect(tokenComplex.ExtractSchema()).toEqual({ + extractSchema_Legacy = { + option0: { 0: 'nat', 1: 'nat', 2: 'timestamp' }, + option1: { 1: 'nat', 2: 'mutez', 3: 'nat', 4: 'timestamp' }, + option2: { 2: 'nat', 3: 'timestamp' }, + option3: { 3: 'nat', 4: 'mutez', 5: 'timestamp' }, + option4: 'nat', + }; + extractSchema_ResetFields = { option0: { 0: 'nat', 1: 'nat', 2: 'timestamp' }, option1: { 0: 'nat', 1: 'mutez', 2: 'nat', 3: 'timestamp' }, option2: { 0: 'nat', 1: 'timestamp' }, option3: { 0: 'nat', 1: 'mutez', 2: 'timestamp' }, option4: 'nat', - }); + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplex.ExtractSchema()).toEqual(extractSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplex.ExtractSchema()).toEqual(extractSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplex.ExtractSchema()).toEqual(extractSchema_ResetFields); - expect(tokenComplex.generateSchema()).toEqual({ + generateSchema_Legacy = { + __michelsonType: 'or', + schema: { + option0: { + __michelsonType: 'pair', + schema: { + 0: { + __michelsonType: 'nat', + schema: 'nat', + }, + 1: { + __michelsonType: 'nat', + schema: 'nat', + }, + 2: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + option1: { + __michelsonType: 'pair', + schema: { + 1: { + __michelsonType: 'nat', + schema: 'nat', + }, + 2: { + __michelsonType: 'mutez', + schema: 'mutez', + }, + 3: { + __michelsonType: 'nat', + schema: 'nat', + }, + 4: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + option2: { + __michelsonType: 'pair', + schema: { + 2: { + __michelsonType: 'nat', + schema: 'nat', + }, + 3: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + option3: { + __michelsonType: 'pair', + schema: { + 3: { + __michelsonType: 'nat', + schema: 'nat', + }, + 4: { + __michelsonType: 'mutez', + schema: 'mutez', + }, + 5: { + __michelsonType: 'timestamp', + schema: 'timestamp', + }, + }, + }, + option4: { + __michelsonType: 'nat', + schema: 'nat', + }, + }, + }; + generateSchema_ResetFields = { __michelsonType: 'or', schema: { option0: { @@ -745,7 +970,13 @@ describe('Or token', () => { schema: 'nat', }, }, - }); + }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenComplex.generateSchema()).toEqual(generateSchema_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenComplex.generateSchema()).toEqual(generateSchema_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenComplex.generateSchema()).toEqual(generateSchema_ResetFields); expect(tokenOrWithOption.generateSchema()).toEqual({ __michelsonType: 'or', @@ -841,22 +1072,29 @@ describe('Or token', () => { ], }) ).toEqual({ myNat: new BigNumber(6) }); // { 0: '6' } - expect( - tokenNestedOr.Execute({ - prim: 'Right', - args: [ - { - prim: 'Left', - args: [ - { - prim: 'Left', - args: [{ prim: 'Pair', args: [{ int: '3' }, { int: '4' }] }], - }, - ], - }, - ], - }) - ).toEqual({ myPair: { 0: new BigNumber(3), 1: new BigNumber(4) } }); // { 4: { myPair: { 4: '3', 5: '4'} } } + let michelson: object = { + prim: 'Right', + args: [ + { + prim: 'Left', + args: [ + { + prim: 'Left', + args: [{ prim: 'Pair', args: [{ int: '3' }, { int: '4' }] }], + }, + ], + }, + ], + }; + let execute_Legacy: object = { myPair: { 4: new BigNumber(3), 5: new BigNumber(4) } }; + let execute_ResetFields: object = { myPair: { 0: new BigNumber(3), 1: new BigNumber(4) } }; + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenNestedOr.Execute(michelson)).toEqual(execute_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenNestedOr.Execute(michelson)).toEqual(execute_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenNestedOr.Execute(michelson)).toEqual(execute_ResetFields); + expect( tokenNestedOr.Execute({ prim: 'Right', @@ -924,22 +1162,31 @@ describe('Or token', () => { ], }) ).toEqual({ 3: new BigNumber(6) }); // { 0: '6' } - expect( - tokenNestedOrWithoutAnnot.Execute({ - prim: 'Right', - args: [ - { - prim: 'Left', - args: [ - { - prim: 'Left', - args: [{ prim: 'Pair', args: [{ int: '3' }, { int: '4' }] }], - }, - ], - }, - ], - }) - ).toEqual({ 4: { 0: new BigNumber(3), 1: new BigNumber(4) } }); // { 4: { myPair: { 4: '3', 5: '4'} } } + + michelson = { + prim: 'Right', + args: [ + { + prim: 'Left', + args: [ + { + prim: 'Left', + args: [{ prim: 'Pair', args: [{ int: '3' }, { int: '4' }] }], + }, + ], + }, + ], + }; + execute_Legacy = { 4: { 4: new BigNumber(3), 5: new BigNumber(4) } }; + execute_ResetFields = { 4: { 0: new BigNumber(3), 1: new BigNumber(4) } }; + + Token.fieldNumberingStrategy = 'Legacy'; + expect(tokenNestedOrWithoutAnnot.Execute(michelson)).toEqual(execute_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(tokenNestedOrWithoutAnnot.Execute(michelson)).toEqual(execute_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(tokenNestedOrWithoutAnnot.Execute(michelson)).toEqual(execute_ResetFields); + expect( tokenNestedOrWithoutAnnot.Execute({ prim: 'Right', @@ -1242,7 +1489,21 @@ describe('Or token', () => { }, ], }; - const expected = { + const execute_Legacy = { + owners: ['tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6'], + inheritors: [], + status: { + rECOVERING: { + 3: 'tz1dcjLdDM6uYKYdQhK177cUbtvL8QwX4ebH', + 4: '2024-04-19T13:53:22.000Z', + }, + }, + quick_recovery_stake: BigNumber(1000000), + quick_recovery_period: BigNumber(0), + direct_debit_mandates: '414522', + direct_debit_mandates_history: '414523', + }; + const execte_ResetFields = { owners: ['tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6'], inheritors: [], status: { @@ -1258,8 +1519,12 @@ describe('Or token', () => { }; const schema = new Schema(schemaObj); - const result = schema.Execute(dataObj); - expect(result).toEqual(expected); + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.Execute(dataObj)).toEqual(execute_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.Execute(dataObj)).toEqual(execte_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.Execute(dataObj)).toEqual(execte_ResetFields); }); it('a smaller reproduction', () => { @@ -1349,7 +1614,17 @@ describe('Or token', () => { }, ], }; - const expected = { + const execute_Legacy = { + owners: ['tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6'], + inheritors: [], + status: { + rECOVERING: { + 3: 'tz1dcjLdDM6uYKYdQhK177cUbtvL8QwX4ebH', + 4: '2024-04-19T13:53:22.000Z', + }, + }, + }; + const execute_ResetFields = { owners: ['tz1aSkwEot3L2kmUvcoxzjMomb9mvBNuzFK6'], inheritors: [], status: { @@ -1361,8 +1636,12 @@ describe('Or token', () => { }; const schema = new Schema(schemaObj); - const result = schema.Execute(dataObj); - expect(result).toEqual(expected); + Token.fieldNumberingStrategy = 'Legacy'; + expect(schema.Execute(dataObj)).toEqual(execute_Legacy); + Token.fieldNumberingStrategy = 'ResetFieldNumbersInNestedObjects'; + expect(schema.Execute(dataObj)).toEqual(execute_ResetFields); + Token.fieldNumberingStrategy = 'Latest'; + expect(schema.Execute(dataObj)).toEqual(execute_ResetFields); }); }); }); diff --git a/packages/taquito/src/taquito.ts b/packages/taquito/src/taquito.ts index 4710b76e26..a15025e70e 100644 --- a/packages/taquito/src/taquito.ts +++ b/packages/taquito/src/taquito.ts @@ -32,8 +32,9 @@ import { ParserProvider } from './parser/interface'; import { MichelCodecParser } from './parser/michel-codec-parser'; import { Injector } from './injector/interface'; import { RpcInjector } from './injector/rpc-injector'; +import { FieldNumberingStrategy, Token } from '@taquito/michelson-encoder'; -export { MichelsonMap, UnitValue } from '@taquito/michelson-encoder'; +export { FieldNumberingStrategy, Token, MichelsonMap, UnitValue } from '@taquito/michelson-encoder'; export { Forger, ForgeParams, ForgeResponse } from '@taquito/local-forging'; export * from './constants'; export * from './context'; @@ -360,6 +361,14 @@ export class TezosToolkit { } } + /** + * @description Sets the strategy used for field numbering in Token execute/encode/decode to convert Michelson values to/from javascript objects + * @param strategy a value of type FieldNumberingStrategy that controls how field numbers are calculated + */ + setFieldNumberingStrategy(strategy: FieldNumberingStrategy) { + Token.fieldNumberingStrategy = strategy; + } + /** * @description Provide access to tezos account management */