git.delta.rocks / unique-network / refs/commits / 33d49899f36c

difftreelog

Merge pull request #985 from UniqueNetwork/fix/xcm-release-60

Yaroslav Bolyukin2023-09-06parents: #432eea3 #bf4df5f.patch.diff
in: master

4 files changed

modified.baedeker/xcm-quartz.jsonnetdiffbeforeafterboth
before · .baedeker/xcm-quartz.jsonnet
1local2m = import 'baedeker-library/mixin/spec.libsonnet',3;45function(relay_spec)67local relay = {8	name: 'relay',9	bin: 'bin/polkadot',10	validatorIdAssignment: 'staking',11	spec: {Genesis:{12		chain: relay_spec,13		modify:: m.genericRelay($, hrmp = std.join([], [14			[[$.parachains[a].paraId, $.parachains[b].paraId, 8, 512], [$.parachains[b].paraId, $.parachains[a].paraId, 8, 512]],15			for [a, b] in [16				['quartz', 'karura'],17				['quartz', 'moonriver'],18				['quartz', 'statemine'],19				['quartz', 'shiden'],20			]21		])),22	}},23	nodes: {24		[name]: {25			bin: $.bin,26			wantedKeys: 'relay',27		},28		for name in ['alice', 'bob', 'charlie', 'dave', 'eve', 'ferdie']29	},30};3132local quartz = {33	name: 'quartz',34	bin: 'bin/unique',35	paraId: 1001,36	spec: {Genesis:{37		modify:: m.genericPara($),38	}},39	nodes: {40		[name]: {41			bin: $.bin,42			wantedKeys: 'para',43		},44		for name in ['alice', 'bob']45	},46};4748local karura = {49	name: 'karura',50	bin: 'bin/acala',51	paraId: 1002,52	spec: {Genesis:{53		chain: 'karura-dev',54		modify:: bdk.mixer([55			m.genericPara($),56			function(prev) prev {id+: '-local'},57		]),58	}},59	nodes: {60		[name]: {61			bin: $.bin,62			wantedKeys: 'para',63			legacyRpc: true,64		},65		for name in ['alice', 'bob']66	},67};6869local moonriver = {70	name: 'moonriver',71	bin: 'bin/moonbeam',72	signatureSchema: 'Ethereum',73	paraId: 1003,74	spec: {Genesis:{75		chain: 'moonriver-local',76		specFilePrefix: 'moonriver-local-',77		modify:: m.genericPara($),78	}},79	nodes: {80		[name]: {81			bin: $.bin,82			wantedKeys: 'para-nimbus',83			legacyRpc: true,84		},85		for name in ['alice', 'bob']86	},87};8889local statemine = {90	name: 'statemine',91	bin: 'bin/cumulus',92	paraId: 1004,93	spec: {Genesis:{94		chain: 'statemine-local',95		modify:: m.genericPara($),96	}},97	nodes: {98		[name]: {99			bin: $.bin,100			wantedKeys: 'para',101		},102		for name in ['alice', 'bob']103	},104};105106local shiden = {107	name: 'shiden',108	bin: 'bin/astar',109	paraId: 1005,110	spec: {Genesis:{111		chain: 'shiden-dev',112		modify:: m.genericPara($),113	}},114	nodes: {115		[name]: {116			bin: $.bin,117			wantedKeys: 'para',118		},119		for name in ['alice', 'bob']120	},121};122123relay + {124	parachains: {125		[para.name]: para,126		for para in [quartz, karura, moonriver, statemine, shiden]127	},128}
modified.baedeker/xcm-unique.jsonnetdiffbeforeafterboth
--- a/.baedeker/xcm-unique.jsonnet
+++ b/.baedeker/xcm-unique.jsonnet
@@ -83,7 +83,7 @@
 			wantedKeys: 'para-nimbus',
 			legacyRpc: true,
 		},
-		for name in ['alice', 'bob']
+		for name in ['alith', 'baltathar']
 	},
 };
 
modifiedtests/src/xcm/xcmQuartz.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmQuartz.test.ts
+++ b/tests/src/xcm/xcmQuartz.test.ts
@@ -467,8 +467,9 @@
   // 2 QTZ sent https://quartz.subscan.io/xcm_message/kusama-f60d821b049f8835a3005ce7102285006f5b61e9
   // 1.919176000000000000 QTZ received (you can check Karura's chain state in the corresponding block)
   const expectedKaruraIncomeFee = 2000000000000000000n - 1919176000000000000n;
+  const karuraEps = 8n * 10n ** 16n;
 
-  const KARURA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedKaruraIncomeFee;
+  let karuraBackwardTransferAmount: bigint;
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
@@ -569,6 +570,8 @@
 
       const karFees = balanceKaruraTokenInit - balanceKaruraTokenMiddle;
       const qtzIncomeTransfer = balanceQuartzForeignTokenMiddle - balanceQuartzForeignTokenInit;
+      karuraBackwardTransferAmount = qtzIncomeTransfer;
+
       const karUnqFees = TRANSFER_AMOUNT - qtzIncomeTransfer;
 
       console.log(
@@ -581,8 +584,11 @@
       );
       console.log('[Quartz -> Karura] income %s QTZ', helper.util.bigIntToDecimals(qtzIncomeTransfer));
       expect(karFees == 0n).to.be.true;
+
+      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
+
       expect(
-        karUnqFees == expectedKaruraIncomeFee,
+        bigintAbs(karUnqFees - expectedKaruraIncomeFee) < karuraEps,
         'Karura took different income fee, check the Karura foreign asset config',
       ).to.be.true;
     });
@@ -611,7 +617,7 @@
         ForeignAsset: 0,
       };
 
-      await helper.xTokens.transfer(randomAccount, id, KARURA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');
+      await helper.xTokens.transfer(randomAccount, id, karuraBackwardTransferAmount, destination, 'Unlimited');
       balanceKaruraTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
       balanceQuartzForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
 
@@ -625,7 +631,7 @@
       console.log('[Karura -> Quartz] outcome %s QTZ', helper.util.bigIntToDecimals(qtzOutcomeTransfer));
 
       expect(karFees > 0, 'Negative fees KAR, looks like nothing was transferred').to.be.true;
-      expect(qtzOutcomeTransfer == KARURA_BACKWARD_TRANSFER_AMOUNT).to.be.true;
+      expect(qtzOutcomeTransfer == karuraBackwardTransferAmount).to.be.true;
     });
 
     await helper.wait.newBlocks(3);
@@ -636,7 +642,7 @@
 
     console.log('[Karura -> Quartz] actually delivered %s QTZ', helper.util.bigIntToDecimals(actuallyDelivered));
 
-    const qtzFees = KARURA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;
+    const qtzFees = karuraBackwardTransferAmount - actuallyDelivered;
     console.log('[Karura -> Quartz] transaction fees on Quartz: %s QTZ', helper.util.bigIntToDecimals(qtzFees));
     expect(qtzFees == 0n).to.be.true;
   });
@@ -673,7 +679,7 @@
     );
 
     let maliciousXcmProgramSent: any;
-    const maxWaitBlocks = 3;
+    const maxWaitBlocks = 5;
 
     // Try to trick Quartz
     await usingKaruraPlaygrounds(karuraUrl, async (helper) => {
@@ -682,8 +688,10 @@
       maliciousXcmProgramSent = await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.XcmpMessageSent);
     });
 
-    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+    await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => {
+      return event.messageHash == maliciousXcmProgramSent.messageHash
+        && event.outcome.isFailedToTransactAsset;
+    });
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -764,7 +772,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -777,7 +785,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -853,7 +861,7 @@
 
   const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
   };
 
   itSub('Quartz rejects KAR tokens from Karura', async ({helper}) => {
@@ -1165,7 +1173,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1254,7 +1262,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1271,7 +1279,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1531,7 +1539,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1612,7 +1620,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1625,7 +1633,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
modifiedtests/src/xcm/xcmUnique.test.tsdiffbeforeafterboth
--- a/tests/src/xcm/xcmUnique.test.ts
+++ b/tests/src/xcm/xcmUnique.test.ts
@@ -84,9 +84,8 @@
 
     await usingRelayPlaygrounds(relayUrl, async (helper) => {
       // Fund accounts on Statemint
-      const relayXcmVersion = 2;
-      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT, relayXcmVersion);
-      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT, relayXcmVersion);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, alice.addressRaw, FUNDING_AMOUNT);
+      await helper.xcm.teleportNativeAsset(alice, STATEMINT_CHAIN, bob.addressRaw, FUNDING_AMOUNT);
     });
 
     await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
@@ -154,7 +153,7 @@
     // (fee for USDT XCM are paid in relay tokens)
     await usingRelayPlaygrounds(relayUrl, async (helper) => {
       const destination = {
-        V1: {
+        V2: {
           parents: 0,
           interior: {X1: {
             Parachain: UNIQUE_CHAIN,
@@ -163,7 +162,7 @@
         }};
 
       const beneficiary = {
-        V1: {
+        V2: {
           parents: 0,
           interior: {X1: {
             AccountId32: {
@@ -175,7 +174,7 @@
       };
 
       const assets = {
-        V1: [
+        V2: [
           {
             id: {
               Concrete: {
@@ -200,7 +199,7 @@
   itSub('Should connect and send USDT from Statemint to Unique', async ({helper}) => {
     await usingStatemintPlaygrounds(statemintUrl, async (helper) => {
       const dest = {
-        V1: {
+        V2: {
           parents: 1,
           interior: {X1: {
             Parachain: UNIQUE_CHAIN,
@@ -209,7 +208,7 @@
         }};
 
       const beneficiary = {
-        V1: {
+        V2: {
           parents: 0,
           interior: {X1: {
             AccountId32: {
@@ -221,7 +220,7 @@
       };
 
       const assets = {
-        V1: [
+        V2: [
           {
             id: {
               Concrete: {
@@ -341,7 +340,7 @@
 
     await usingRelayPlaygrounds(relayUrl, async (helper) => {
       const destination = {
-        V1: {
+        V2: {
           parents: 0,
           interior: {X1: {
             Parachain: UNIQUE_CHAIN,
@@ -350,7 +349,7 @@
         }};
 
       const beneficiary = {
-        V1: {
+        V2: {
           parents: 0,
           interior: {X1: {
             AccountId32: {
@@ -362,7 +361,7 @@
       };
 
       const assets = {
-        V1: [
+        V2: [
           {
             id: {
               Concrete: {
@@ -468,8 +467,9 @@
   // 2 UNQ sent https://unique.subscan.io/xcm_message/polkadot-bad0b68847e2398af25d482e9ee6f9c1f9ec2a48
   // 1.898970000000000000 UNQ received (you can check Acala's chain state in the corresponding block)
   const expectedAcalaIncomeFee = 2000000000000000000n - 1898970000000000000n;
+  const acalaEps = 8n * 10n ** 16n;
 
-  const ACALA_BACKWARD_TRANSFER_AMOUNT = TRANSFER_AMOUNT - expectedAcalaIncomeFee;
+  let acalaBackwardTransferAmount: bigint;
 
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
@@ -482,7 +482,7 @@
 
     await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
       const destination = {
-        V1: {
+        V2: {
           parents: 1,
           interior: {
             X1: {
@@ -571,6 +571,8 @@
 
       const acaFees = balanceAcalaTokenInit - balanceAcalaTokenMiddle;
       const unqIncomeTransfer = balanceUniqueForeignTokenMiddle - balanceUniqueForeignTokenInit;
+      acalaBackwardTransferAmount = unqIncomeTransfer;
+
       const acaUnqFees = TRANSFER_AMOUNT - unqIncomeTransfer;
 
       console.log(
@@ -583,8 +585,11 @@
       );
       console.log('[Unique -> Acala] income %s UNQ', helper.util.bigIntToDecimals(unqIncomeTransfer));
       expect(acaFees == 0n).to.be.true;
+
+      const bigintAbs = (n: bigint) => (n < 0n) ? -n : n;
+
       expect(
-        acaUnqFees == expectedAcalaIncomeFee,
+        bigintAbs(acaUnqFees - expectedAcalaIncomeFee) < acalaEps,
         'Acala took different income fee, check the Acala foreign asset config',
       ).to.be.true;
     });
@@ -593,7 +598,7 @@
   itSub('Should connect to Acala and send UNQ back', async ({helper}) => {
     await usingAcalaPlaygrounds(acalaUrl, async (helper) => {
       const destination = {
-        V1: {
+        V2: {
           parents: 1,
           interior: {
             X2: [
@@ -613,7 +618,7 @@
         ForeignAsset: 0,
       };
 
-      await helper.xTokens.transfer(randomAccount, id, ACALA_BACKWARD_TRANSFER_AMOUNT, destination, 'Unlimited');
+      await helper.xTokens.transfer(randomAccount, id, acalaBackwardTransferAmount, destination, 'Unlimited');
       balanceAcalaTokenFinal = await helper.balance.getSubstrate(randomAccount.address);
       balanceUniqueForeignTokenFinal = await helper.tokens.accounts(randomAccount.address, id);
 
@@ -627,7 +632,7 @@
       console.log('[Acala -> Unique] outcome %s UNQ', helper.util.bigIntToDecimals(unqOutcomeTransfer));
 
       expect(acaFees > 0, 'Negative fees ACA, looks like nothing was transferred').to.be.true;
-      expect(unqOutcomeTransfer == ACALA_BACKWARD_TRANSFER_AMOUNT).to.be.true;
+      expect(unqOutcomeTransfer == acalaBackwardTransferAmount).to.be.true;
     });
 
     await helper.wait.newBlocks(3);
@@ -638,7 +643,7 @@
 
     console.log('[Acala -> Unique] actually delivered %s UNQ', helper.util.bigIntToDecimals(actuallyDelivered));
 
-    const unqFees = ACALA_BACKWARD_TRANSFER_AMOUNT - actuallyDelivered;
+    const unqFees = acalaBackwardTransferAmount - actuallyDelivered;
     console.log('[Acala -> Unique] transaction fees on Unique: %s UNQ', helper.util.bigIntToDecimals(unqFees));
     expect(unqFees == 0n).to.be.true;
   });
@@ -655,7 +660,7 @@
     const [targetAccount] = await helper.arrange.createAccounts([targetAccountBalance], alice);
 
     const uniqueMultilocation = {
-      V1: {
+      V2: {
         parents: 1,
         interior: {
           X1: {Parachain: UNIQUE_CHAIN},
@@ -685,7 +690,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -718,7 +723,7 @@
     const [targetAccount] = await helper.arrange.createAccounts([0n], alice);
 
     const uniqueMultilocation = {
-      V1: {
+      V2: {
         parents: 1,
         interior: {
           X1: {
@@ -766,7 +771,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -779,7 +784,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -845,7 +850,7 @@
       };
 
       uniqueCombinedMultilocationAcala = {
-        V1: {
+        V2: {
           parents: 1,
           interior: {
             X2: [uniqueParachainJunction, uniqueAccountJunction],
@@ -865,7 +870,7 @@
 
   const expectFailedToTransact = async (helper: DevUniqueHelper, messageSent: any) => {
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == messageSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
   };
 
   itSub('Unique rejects ACA tokens from Acala', async ({helper}) => {
@@ -1178,7 +1183,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1267,7 +1272,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1284,7 +1289,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1543,7 +1548,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramSent.messageHash
-        && event.outcome().isFailedToTransactAsset);
+        && event.outcome.isFailedToTransactAsset);
 
     targetAccountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(targetAccountBalance).to.be.equal(0n);
@@ -1624,7 +1629,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramFullIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     let accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);
@@ -1637,7 +1642,7 @@
     });
 
     await helper.wait.expectEvent(maxWaitBlocks, Event.XcmpQueue.Fail, event => event.messageHash == maliciousXcmProgramHereIdSent.messageHash
-        && event.outcome().isUntrustedReserveLocation);
+        && event.outcome.isUntrustedReserveLocation);
 
     accountBalance = await helper.balance.getSubstrate(targetAccount.address);
     expect(accountBalance).to.be.equal(0n);