git.delta.rocks / unique-network / refs/commits / d65670117bc2

difftreelog

source

tests/src/limits.test.ts22.5 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import usingApi from './substrate/substrate-api';19import {20  createCollectionExpectSuccess,21  destroyCollectionExpectSuccess,22  setCollectionLimitsExpectSuccess,23  setCollectionSponsorExpectSuccess,24  confirmSponsorshipExpectSuccess,25  createItemExpectSuccess,26  createItemExpectFailure,27  transferExpectSuccess,28  getFreeBalance,29  waitNewBlocks, burnItemExpectSuccess,30  requirePallets,31  Pallets,32} from './util/helpers';33import {expect} from 'chai';3435describe('Number of tokens per address (NFT)', () => {36  let alice: IKeyringPair;3738  before(async () => {39    await usingApi(async (api, privateKeyWrapper) => {40      alice = privateKeyWrapper('//Alice');41    });42  });4344  it.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async () => {4546    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});47    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 20});48    for(let i = 0; i < 10; i++){49      await createItemExpectSuccess(alice, collectionId, 'NFT');50    }51    await createItemExpectFailure(alice, collectionId, 'NFT');52    for(let i = 1; i < 11; i++) {53      await burnItemExpectSuccess(alice, collectionId, i);54    }55    await destroyCollectionExpectSuccess(collectionId);56  });5758  it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => {5960    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});61    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 1});62    await createItemExpectSuccess(alice, collectionId, 'NFT');63    await createItemExpectFailure(alice, collectionId, 'NFT');64    await burnItemExpectSuccess(alice, collectionId, 1);65    await destroyCollectionExpectSuccess(collectionId);66  });67});6869describe('Number of tokens per address (ReFungible)', () => {70  let alice: IKeyringPair;7172  before(async function() {73    await requirePallets(this, [Pallets.ReFungible]);7475    await usingApi(async (api, privateKeyWrapper) => {76      alice = privateKeyWrapper('//Alice');77    });78  });7980  it.skip('Collection limits allow greater number than chain limits, chain limits are enforced', async () => {81    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});82    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 20});83    for(let i = 0; i < 10; i++){84      await createItemExpectSuccess(alice, collectionId, 'ReFungible');85    }86    await createItemExpectFailure(alice, collectionId, 'ReFungible');87    for(let i = 1; i < 11; i++) {88      await burnItemExpectSuccess(alice, collectionId, i, 100);89    }90    await destroyCollectionExpectSuccess(collectionId);91  });9293  it('Collection limits allow lower number than chain limits, collection limits are enforced', async () => {94    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});95    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 1});96    await createItemExpectSuccess(alice, collectionId, 'ReFungible');97    await createItemExpectFailure(alice, collectionId, 'ReFungible');98    await burnItemExpectSuccess(alice, collectionId, 1, 100);99    await destroyCollectionExpectSuccess(collectionId);100  });101});102103describe.skip('Sponsor timeout (NFT) (only for special chain limits test)', () => {104  let alice: IKeyringPair;105  let bob: IKeyringPair;106  let charlie: IKeyringPair;107108  before(async () => {109    await usingApi(async (api, privateKeyWrapper) => {110      alice = privateKeyWrapper('//Alice');111      bob = privateKeyWrapper('//Bob');112      charlie = privateKeyWrapper('//Charlie');113    });114  });115116  it.skip('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {117    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});118    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});119    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');120    await setCollectionSponsorExpectSuccess(collectionId, alice.address);121    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');122    await transferExpectSuccess(collectionId, tokenId, alice, bob);123    const aliceBalanceBefore = await getFreeBalance(alice);124125    // check setting SponsorTimeout = 5, fail126    await waitNewBlocks(5);127    await transferExpectSuccess(collectionId, tokenId, bob, charlie);128    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);129    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);130131    // check setting SponsorTimeout = 7, success132    await waitNewBlocks(2); // 5 + 2133    await transferExpectSuccess(collectionId, tokenId, charlie, bob);134    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);135    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;136    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);137    await destroyCollectionExpectSuccess(collectionId);138  });139140  it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {141142    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});143    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});144    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');145    await setCollectionSponsorExpectSuccess(collectionId, alice.address);146    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');147    await transferExpectSuccess(collectionId, tokenId, alice, bob);148    const aliceBalanceBefore = await getFreeBalance(alice);149150    // check setting SponsorTimeout = 1, fail151    await waitNewBlocks(1);152    await transferExpectSuccess(collectionId, tokenId, bob, charlie);153    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);154    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);155156    // check setting SponsorTimeout = 5, success157    await waitNewBlocks(4);158    await transferExpectSuccess(collectionId, tokenId, charlie, bob);159    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);160    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;161    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);162    await destroyCollectionExpectSuccess(collectionId);163  });164});165166describe.skip('Sponsor timeout (Fungible) (only for special chain limits test)', () => {167  let alice: IKeyringPair;168  let bob: IKeyringPair;169  let charlie: IKeyringPair;170171  before(async () => {172    await usingApi(async (api, privateKeyWrapper) => {173      alice = privateKeyWrapper('//Alice');174      bob = privateKeyWrapper('//Bob');175      charlie = privateKeyWrapper('//Charlie');176    });177  });178179  it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {180    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});181    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});182    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');183    await setCollectionSponsorExpectSuccess(collectionId, alice.address);184    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');185    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');186    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');187    const aliceBalanceBefore = await getFreeBalance(alice);188189    // check setting SponsorTimeout = 5, fail190    await waitNewBlocks(5);191    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');192    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);193    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);194195    // check setting SponsorTimeout = 7, success196    await waitNewBlocks(2); // 5 + 2197    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');198    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);199    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;200    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);201202    await destroyCollectionExpectSuccess(collectionId);203  });204205  it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {206207    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});208    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});209    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');210    await setCollectionSponsorExpectSuccess(collectionId, alice.address);211    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');212    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');213    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');214    const aliceBalanceBefore = await getFreeBalance(alice);215216    // check setting SponsorTimeout = 1, fail217    await waitNewBlocks(1);218    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');219    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);220    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);221222    // check setting SponsorTimeout = 5, success223    await waitNewBlocks(4);224    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');225    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);226    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;227    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);228229    await destroyCollectionExpectSuccess(collectionId);230  });231});232233describe.skip('Sponsor timeout (ReFungible) (only for special chain limits test)', () => {234  let alice: IKeyringPair;235  let bob: IKeyringPair;236  let charlie: IKeyringPair;237238  before(async () => {239    await usingApi(async (api, privateKeyWrapper) => {240      alice = privateKeyWrapper('//Alice');241      bob = privateKeyWrapper('//Bob');242      charlie = privateKeyWrapper('//Charlie');243    });244  });245246  it('Collection limits have greater timeout value than chain limits, collection limits are enforced', async () => {247    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});248    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 7});249    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');250    await setCollectionSponsorExpectSuccess(collectionId, alice.address);251    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');252    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');253    const aliceBalanceBefore = await getFreeBalance(alice);254255    // check setting SponsorTimeout = 5, fail256    await waitNewBlocks(5);257    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');258    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);259    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);260261    // check setting SponsorTimeout = 7, success262    await waitNewBlocks(2); // 5 + 2263    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');264    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);265    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;266    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);267    await destroyCollectionExpectSuccess(collectionId);268  });269270  it('Collection limits have lower timeout value than chain limits, chain limits are enforced', async () => {271272    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});273    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 1});274    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');275    await setCollectionSponsorExpectSuccess(collectionId, alice.address);276    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');277    await transferExpectSuccess(collectionId, tokenId, alice, bob);278    const aliceBalanceBefore = await getFreeBalance(alice);279280    // check setting SponsorTimeout = 1, fail281    await waitNewBlocks(1);282    await transferExpectSuccess(collectionId, tokenId, bob, charlie);283    const aliceBalanceAfterUnsponsoredTransaction = await getFreeBalance(alice);284    expect(aliceBalanceAfterUnsponsoredTransaction).to.be.equals(aliceBalanceBefore);285286    // check setting SponsorTimeout = 5, success287    await waitNewBlocks(4);288    await transferExpectSuccess(collectionId, tokenId, charlie, bob);289    const aliceBalanceAfterSponsoredTransaction = await getFreeBalance(alice);290    expect(aliceBalanceAfterSponsoredTransaction < aliceBalanceBefore).to.be.true;291    //expect(aliceBalanceAfterSponsoredTransaction).to.be.lessThan(aliceBalanceBefore);292    await destroyCollectionExpectSuccess(collectionId);293  });294});295296describe('Collection zero limits (NFT)', () => {297  let alice: IKeyringPair;298  let bob: IKeyringPair;299  let charlie: IKeyringPair;300301  before(async () => {302    await usingApi(async (api, privateKeyWrapper) => {303      alice = privateKeyWrapper('//Alice');304      bob = privateKeyWrapper('//Bob');305      charlie = privateKeyWrapper('//Charlie');306    });307  });308309  it.skip('Limits have 0 in tokens per address field, the chain limits are applied', async () => {310    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});311    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 0});312    for(let i = 0; i < 10; i++){313      await createItemExpectSuccess(alice, collectionId, 'NFT');314    }315    await createItemExpectFailure(alice, collectionId, 'NFT');316  });317318  it('Limits have 0 in sponsor timeout, no limits are applied', async () => {319320    const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});321    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0});322    const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');323    await setCollectionSponsorExpectSuccess(collectionId, alice.address);324    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');325    await transferExpectSuccess(collectionId, tokenId, alice, bob);326    const aliceBalanceBefore = await getFreeBalance(alice);327328    // check setting SponsorTimeout = 0, success with next block329    await waitNewBlocks(1);330    await transferExpectSuccess(collectionId, tokenId, bob, charlie);331    const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice);332    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;333    //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);334  });335});336337describe('Collection zero limits (Fungible)', () => {338  let alice: IKeyringPair;339  let bob: IKeyringPair;340  let charlie: IKeyringPair;341342  before(async () => {343    await usingApi(async (api, privateKeyWrapper) => {344      alice = privateKeyWrapper('//Alice');345      bob = privateKeyWrapper('//Bob');346      charlie = privateKeyWrapper('//Charlie');347    });348  });349350  it('Limits have 0 in sponsor timeout, no limits are applied', async () => {351    const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});352    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0});353    const tokenId = await createItemExpectSuccess(alice, collectionId, 'Fungible');354    await setCollectionSponsorExpectSuccess(collectionId, alice.address);355    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');356    await transferExpectSuccess(collectionId, tokenId, alice, bob, 10, 'Fungible');357    const aliceBalanceBefore = await getFreeBalance(alice);358    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');359360    // check setting SponsorTimeout = 0, success with next block361    await waitNewBlocks(1);362    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 2, 'Fungible');363    const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice);364    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;365    //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);366  });367});368369describe('Collection zero limits (ReFungible)', () => {370  let alice: IKeyringPair;371  let bob: IKeyringPair;372  let charlie: IKeyringPair;373374  before(async function() {375    await requirePallets(this, [Pallets.ReFungible]);376377    await usingApi(async (api, privateKeyWrapper) => {378      alice = privateKeyWrapper('//Alice');379      bob = privateKeyWrapper('//Bob');380      charlie = privateKeyWrapper('//Charlie');381    });382  });383384  it.skip('Limits have 0 in tokens per address field, the chain limits are applied', async () => {385    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});386    await setCollectionLimitsExpectSuccess(alice, collectionId, {accountTokenOwnershipLimit: 0});387    for(let i = 0; i < 10; i++){388      await createItemExpectSuccess(alice, collectionId, 'ReFungible');389    }390    await createItemExpectFailure(alice, collectionId, 'ReFungible');391  });392393  it('Limits have 0 in sponsor timeout, no limits are applied', async () => {394395    const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});396    await setCollectionLimitsExpectSuccess(alice, collectionId, {sponsorTransferTimeout: 0});397    const tokenId = await createItemExpectSuccess(alice, collectionId, 'ReFungible');398    await setCollectionSponsorExpectSuccess(collectionId, alice.address);399    await confirmSponsorshipExpectSuccess(collectionId, '//Alice');400    await transferExpectSuccess(collectionId, tokenId, alice, bob, 100, 'ReFungible');401    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');402    const aliceBalanceBefore = await getFreeBalance(alice);403404    // check setting SponsorTimeout = 0, success with next block405    await waitNewBlocks(1);406    await transferExpectSuccess(collectionId, tokenId, bob, charlie, 20, 'ReFungible');407    const aliceBalanceAfterSponsoredTransaction1 = await getFreeBalance(alice);408    expect(aliceBalanceAfterSponsoredTransaction1 < aliceBalanceBefore).to.be.true;409    //expect(aliceBalanceAfterSponsoredTransaction1).to.be.lessThan(aliceBalanceBefore);410  });411  412  it('Effective collection limits', async () => {413    await usingApi(async (api) => {414      const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});415      await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});416      417      { // Check that limits is undefined418        const collection = await api.rpc.unique.collectionById(collectionId);419        expect(collection.isSome).to.be.true;420        const limits = collection.unwrap().limits;421        expect(limits).to.be.any;422        423        expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.null;424        expect(limits.sponsoredDataSize.toHuman()).to.be.null;425        expect(limits.sponsoredDataRateLimit.toHuman()).to.be.null;426        expect(limits.tokenLimit.toHuman()).to.be.null;427        expect(limits.sponsorTransferTimeout.toHuman()).to.be.null;428        expect(limits.sponsorApproveTimeout.toHuman()).to.be.null;429        expect(limits.ownerCanTransfer.toHuman()).to.be.true;430        expect(limits.ownerCanDestroy.toHuman()).to.be.null;431        expect(limits.transfersEnabled.toHuman()).to.be.null;432      }433  434      { // Check that limits is undefined for non-existent collection435        const limits = await api.rpc.unique.effectiveCollectionLimits(11111);436        expect(limits.toHuman()).to.be.null;437      }438  439      { // Check that default values defined for collection limits440        const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId);441        expect(limitsOpt.isNone).to.be.false;442        const limits = limitsOpt.unwrap();443  444        expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('100,000');445        expect(limits.sponsoredDataSize.toHuman()).to.be.eq('2,048');446        expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled');447        expect(limits.tokenLimit.toHuman()).to.be.eq('4,294,967,295');448        expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5');449        expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5');450        expect(limits.ownerCanTransfer.toHuman()).to.be.true;451        expect(limits.ownerCanDestroy.toHuman()).to.be.true;452        expect(limits.transfersEnabled.toHuman()).to.be.true;453      }454455      { //Check the values for collection limits456        await setCollectionLimitsExpectSuccess(alice, collectionId, {457          accountTokenOwnershipLimit: 99_999,458          sponsoredDataSize: 1024,459          tokenLimit: 123,460          transfersEnabled: false,461        });462463        const limitsOpt = await api.rpc.unique.effectiveCollectionLimits(collectionId);464        expect(limitsOpt.isNone).to.be.false;465        const limits = limitsOpt.unwrap();466  467        expect(limits.accountTokenOwnershipLimit.toHuman()).to.be.eq('99,999');468        expect(limits.sponsoredDataSize.toHuman()).to.be.eq('1,024');469        expect(limits.sponsoredDataRateLimit.toHuman()).to.be.eq('SponsoringDisabled');470        expect(limits.tokenLimit.toHuman()).to.be.eq('123');471        expect(limits.sponsorTransferTimeout.toHuman()).to.be.eq('5');472        expect(limits.sponsorApproveTimeout.toHuman()).to.be.eq('5');473        expect(limits.ownerCanTransfer.toHuman()).to.be.true;474        expect(limits.ownerCanDestroy.toHuman()).to.be.true;475        expect(limits.transfersEnabled.toHuman()).to.be.false;476      }477    });478  });479});480481