ethers/lib/utils#solidityPack TypeScript Examples

The following examples show how to use ethers/lib/utils#solidityPack. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: sign-utils.ts    From shoyu with MIT License 7 votes vote down vote up
getDigest = async (
    provider: any,
    name: string,   // name is deprecated
    contractAddress: string,
    hash: BytesLike
): Promise<string> => {
    return keccak256(
        solidityPack(
            ["bytes1", "bytes1", "bytes32", "bytes32"],
            ["0x19", "0x01", await domainSeparator(provider, name, contractAddress), hash]
        )
    );
}
Example #2
Source File: signatures.ts    From ERC20Permit with GNU General Public License v3.0 6 votes vote down vote up
// Returns the EIP712 hash which should be signed by the user
// in order to make a call to `permit`
export function getPermitDigest(
  name: string,
  address: string,
  chainId: number,
  approve: {
    owner: string
    spender: string
    value: BigNumberish
  },
  nonce: BigNumberish,
  deadline: BigNumberish
) {
  const DOMAIN_SEPARATOR = getDomainSeparator(name, address, chainId)
  return keccak256(
    solidityPack(
      ['bytes1', 'bytes1', 'bytes32', 'bytes32'],
      [
        '0x19',
        '0x01',
        DOMAIN_SEPARATOR,
        keccak256(
          defaultAbiCoder.encode(
            ['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'],
            [PERMIT_TYPEHASH, approve.owner, approve.spender, approve.value, nonce, deadline]
          )
        ),
      ]
    )
  )
}
Example #3
Source File: transfer.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public message(nonce: BigNumber): string {
        return solidityPack(
            ["uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
            [
                this.txType,
                this.fromIndex,
                this.toIndex,
                nonce,
                this.amount,
                this.fee
            ]
        );
    }
Example #4
Source File: factory.ts    From hubble-contracts with MIT License 6 votes vote down vote up
function create2TransferMessage(
    tx: TxCreate2Transfer,
    pubkeyHash: string
): string {
    return solidityPack(
        ["uint256", "uint256", "bytes32", "uint256", "uint256", "uint256"],
        ["0x03", tx.fromIndex, pubkeyHash, tx.nonce, tx.amount, tx.fee]
    );
}
Example #5
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public message(): string {
        return solidityPack(
            ["uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
            [
                this.TX_TYPE,
                this.fromIndex,
                this.toIndex,
                this.nonce,
                this.amount,
                this.fee
            ]
        );
    }
Example #6
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public encodeOffchain() {
        return solidityPack(
            ["uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
            [
                this.TX_TYPE,
                this.fromIndex,
                this.toIndex,
                this.amount,
                this.fee,
                this.nonce
            ]
        );
    }
Example #7
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public message(): string {
        return solidityPack(
            ["uint8", "uint32", "uint256", "uint256", "uint32", "uint32"],
            [
                this.TX_TYPE,
                this.fromIndex,
                this.amount,
                this.fee,
                this.nonce,
                this.spokeID
            ]
        );
    }
Example #8
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public encodeOffchain() {
        return solidityPack(
            ["uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
            [
                this.TX_TYPE,
                this.fromIndex,
                this.amount,
                this.fee,
                this.spokeID,
                this.nonce
            ]
        );
    }
Example #9
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public message(): string {
        return solidityPack(
            ["uint256", "uint256", "bytes32", "uint256", "uint256", "uint256"],
            [
                this.TX_TYPE,
                this.fromIndex,
                hashPubkey(this.toPubkey),
                this.nonce,
                this.amount,
                this.fee
            ]
        );
    }
Example #10
Source File: tx.ts    From hubble-contracts with MIT License 6 votes vote down vote up
public encodeOffchain() {
        return solidityPack(
            [
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "uint256",
                "uint256"
            ],
            [
                this.TX_TYPE,
                this.fromIndex,
                this.toIndex,
                this.toPubkeyID,
                this.amount,
                this.fee,
                this.nonce
            ]
        );
    }
Example #11
Source File: pubkey.ts    From hubble-contracts with MIT License 5 votes vote down vote up
public encode(): string {
        return solidityPack(solidityPubkeyType, this.pubkey);
    }
Example #12
Source File: state.ts    From hubble-contracts with MIT License 5 votes vote down vote up
public encode(): string {
        return solidityPack(
            ["uint256", "uint256", "uint256", "uint256"],
            [this.pubkeyID, this.tokenID, this.balance, this.nonce]
        );
    }
Example #13
Source File: fliRebalanceViewer.spec.ts    From index-coop-smart-contracts with Apache License 2.0 4 votes vote down vote up
describe("FLIRebalanceViewer", async () => {

  let owner: Account;
  let deployer: DeployHelper;

  let setV2Setup: SetFixture;
  let uniswapV2Setup: UniswapFixture;
  let uniswapV3Setup: UniswapV3Fixture;

  let fliExtensionMock: FLIStrategyExtensionMock;
  let fliViewer: FLIRebalanceViewer;

  let uniswapV2ExchangeName: string;
  let uniswapV3ExchangeName: string;

  before(async () => {
    [ owner ] = await getAccounts();

    deployer = new DeployHelper(owner.wallet);

    setV2Setup = getSetFixture(owner.address);
    uniswapV2Setup = getUniswapFixture(owner.address);
    uniswapV3Setup = getUniswapV3Fixture(owner.address);

    await setV2Setup.initialize();
    await uniswapV2Setup.initialize(owner, setV2Setup.weth.address, setV2Setup.wbtc.address, setV2Setup.usdc.address, false);
    await uniswapV3Setup.initialize(owner, setV2Setup.weth, 2000, setV2Setup.wbtc, 35000, setV2Setup.dai);

    uniswapV2ExchangeName = "UniswapV2ExchangeAdapter";
    uniswapV3ExchangeName = "UniswapV3ExchangeAdapter";

    fliExtensionMock = await deployer.mocks.deployFLIStrategyExtensionMock();
    fliViewer = await deployer.viewers.deployFLIRebalanceViewer(
      fliExtensionMock.address,
      uniswapV3Setup.quoter.address,
      uniswapV2Setup.router.address,
      uniswapV3ExchangeName,
      uniswapV2ExchangeName
    );

    // Setup FLI extension mock
    const strategy: ContractSettings = {
      setToken: await getRandomAddress(),
      leverageModule: await getRandomAddress(),
      comptroller: await getRandomAddress(),
      collateralPriceOracle: await getRandomAddress(),
      borrowPriceOracle: await getRandomAddress(),
      targetCollateralCToken: await getRandomAddress(),
      targetBorrowCToken: await getRandomAddress(),
      collateralAsset: setV2Setup.weth.address,
      borrowAsset: setV2Setup.usdc.address,
      collateralDecimalAdjustment: BigNumber.from(10),
      borrowDecimalAdjustment: BigNumber.from(22),
    };

    const uniV2ExchangeSettings: ExchangeSettings = {
      twapMaxTradeSize: ether(100),
      incentivizedTwapMaxTradeSize: ether(100),
      exchangeLastTradeTimestamp: BigNumber.from(0),
      leverExchangeData: EMPTY_BYTES,
      deleverExchangeData: EMPTY_BYTES,
    };

    const uniV3LeverData = solidityPack(
      ["address", "uint24", "address"],
      [setV2Setup.usdc.address, BigNumber.from(3000), setV2Setup.weth.address]
    );
    const uniV3DeleverData = solidityPack(
      ["address", "uint24", "address"],
      [setV2Setup.weth.address, BigNumber.from(3000), setV2Setup.usdc.address]
    );
    const uniV3ExchangeSettings: ExchangeSettings = {
      twapMaxTradeSize: ether(100),
      incentivizedTwapMaxTradeSize: ether(100),
      exchangeLastTradeTimestamp: BigNumber.from(0),
      leverExchangeData: uniV3LeverData,
      deleverExchangeData: uniV3DeleverData,
    };

    await fliExtensionMock.setExchangeSettings(uniswapV2ExchangeName, uniV2ExchangeSettings);
    await fliExtensionMock.setExchangeSettings(uniswapV3ExchangeName, uniV3ExchangeSettings);
    await fliExtensionMock.setStrategy(strategy);
  });

  addSnapshotBeforeRestoreAfterEach();

  describe("#constructor", async () => {

    let subjectFLIStrategyExtension: Address;
    let subjectUniV3Quoter: Address;
    let subjectUniV2Router: Address;
    let subjectUniV3Name: string;
    let subjectUniV2Name: string;

    beforeEach(async () => {
      subjectFLIStrategyExtension = await getRandomAddress();
      subjectUniV3Quoter = await getRandomAddress();
      subjectUniV2Router =  await getRandomAddress();
      subjectUniV3Name = uniswapV3ExchangeName;
      subjectUniV2Name = uniswapV2ExchangeName;
    });

    async function subject(): Promise<FLIRebalanceViewer> {
      return deployer.viewers.deployFLIRebalanceViewer(
        subjectFLIStrategyExtension,
        subjectUniV3Quoter,
        subjectUniV2Router,
        subjectUniV3Name,
        subjectUniV2Name
      );
    }

    it("should set the correct state variables", async () => {
      const viewer = await subject();

      expect(await viewer.fliStrategyExtension()).to.eq(subjectFLIStrategyExtension);
      expect(await viewer.uniswapV3Quoter()).to.eq(subjectUniV3Quoter);
      expect(await viewer.uniswapV2Router()).to.eq(subjectUniV2Router);
      expect(await viewer.uniswapV3ExchangeName()).to.eq(subjectUniV3Name);
      expect(await viewer.uniswapV2ExchangeName()).to.eq(subjectUniV2Name);
    });
  });

  describe("#shouldRebalanceWithBound", async () => {

    let subjectMinLeverageRatio: BigNumber;
    let subjectMaxLeverageRatio: BigNumber;

    beforeEach(async () => {
      subjectMinLeverageRatio = ether(1.7);
      subjectMaxLeverageRatio = ether(2.3);

      const shouldRebalanceNames = [ uniswapV3ExchangeName, uniswapV2ExchangeName ];
      const shouldRebalanceEnums = [ 1, 1 ];

      await fliExtensionMock.setShouldRebalanceWithBounds(shouldRebalanceNames, shouldRebalanceEnums);

      await setV2Setup.weth.approve(uniswapV2Setup.router.address, MAX_UINT_256);
      await setV2Setup.usdc.approve(uniswapV2Setup.router.address, MAX_UINT_256);
      await setV2Setup.wbtc.approve(uniswapV2Setup.router.address, MAX_UINT_256);
      await setV2Setup.weth.approve(uniswapV3Setup.nftPositionManager.address, MAX_UINT_256);
      await setV2Setup.usdc.approve(uniswapV3Setup.nftPositionManager.address, MAX_UINT_256);
    });

    async function subject(): Promise<[string[], number[]]> {
      return await fliViewer.callStatic.shouldRebalanceWithBounds(subjectMinLeverageRatio, subjectMaxLeverageRatio);
    }

    context("when delevering", async () => {

      beforeEach(async () => {
        const chunkRebalanceSizes = [ ether(5), ether(3) ];
        const chunkRebalanceSellAsset = setV2Setup.weth.address;
        const chunkRebalanceBuyAsset = setV2Setup.usdc.address;

        await fliExtensionMock.setGetChunkRebalanceWithBounds(chunkRebalanceSizes, chunkRebalanceSellAsset, chunkRebalanceBuyAsset);
      });

      context("when Uniswap V3 will produce a better trade", async () => {

        beforeEach(async () => {
          await uniswapV2Setup.router.addLiquidity(
            setV2Setup.weth.address,
            setV2Setup.usdc.address,
            ether(100),
            usdc(200_000),
            0,
            0,
            owner.address,
            MAX_UINT_256
          );

          await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
          await uniswapV3Setup.addLiquidityWide(
            setV2Setup.weth,
            setV2Setup.usdc,
            3000,
            ether(1000),
            usdc(2_000_000),
            owner.address
          );
        });

        it("should set Uniswap V3 as the preferred exchange", async () => {
          const [ exchangeNames, rebalanceEnums ] = await subject();

          expect(exchangeNames[0]).to.eq(uniswapV3ExchangeName);
          expect(rebalanceEnums[0]).to.eq(1);

          expect(exchangeNames[1]).to.eq(uniswapV2ExchangeName);
          expect(rebalanceEnums[1]).to.eq(1);
        });
      });

      context("when Uniswap V2 will produce a better trade", async () => {

        beforeEach(async () => {
          await uniswapV2Setup.router.addLiquidity(
            setV2Setup.weth.address,
            setV2Setup.usdc.address,
            ether(1000),
            usdc(2_000_000),
            0,
            0,
            owner.address,
            MAX_UINT_256
          );

          await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
          await uniswapV3Setup.addLiquidityWide(
            setV2Setup.weth,
            setV2Setup.usdc,
            3000,
            ether(100),
            usdc(200_000),
            owner.address
          );
        });

        it("should set Uniswap V2 as the preferred exchange", async () => {
          const [ exchangeNames, rebalanceEnums ] = await subject();

          expect(exchangeNames[0]).to.eq(uniswapV2ExchangeName);
          expect(rebalanceEnums[0]).to.eq(1);

          expect(exchangeNames[1]).to.eq(uniswapV3ExchangeName);
          expect(rebalanceEnums[1]).to.eq(1);
        });
      });

      context("when Uniswap V3 should rebalance, but V2 should not", async () => {

        beforeEach(async () => {
          const shouldRebalanceNames = [ uniswapV3ExchangeName, uniswapV2ExchangeName ];
          const shouldRebalanceEnums = [ 1, 0 ];

          await fliExtensionMock.setShouldRebalanceWithBounds(shouldRebalanceNames, shouldRebalanceEnums);
        });

        context("when Uniswap V3 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.usdc.address,
              ether(100),
              usdc(200_000),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(1000),
              usdc(2_000_000),
              owner.address
            );
          });

          it("should set Uniswap V3 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[1]).to.eq(0);
          });
        });

        context("when Uniswap V2 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.usdc.address,
              ether(1000),
              usdc(2_000_000),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(100),
              usdc(200_000),
              owner.address
            );
          });

          it("should set Uniswap V3 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[1]).to.eq(0);
          });
        });
      });

      context("when Uniswap V2 should rebalance, but V3 should not", async () => {

        beforeEach(async () => {
          const shouldRebalanceNames = [ uniswapV3ExchangeName, uniswapV2ExchangeName ];
          const shouldRebalanceEnums = [ 0, 1 ];

          await fliExtensionMock.setShouldRebalanceWithBounds(shouldRebalanceNames, shouldRebalanceEnums);
        });

        context("when Uniswap V3 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.usdc.address,
              ether(100),
              usdc(200_000),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(1000),
              usdc(2_000_000),
              owner.address
            );
          });

          it("should set Uniswap V2 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[1]).to.eq(0);
          });
        });

        context("when Uniswap V2 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.usdc.address,
              ether(1000),
              usdc(2_000_000),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(100),
              usdc(200_000),
              owner.address
            );
          });

          it("should set Uniswap V2 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[1]).to.eq(0);
          });
        });
      });

      context("when the rebalance will execute a multi-hop trade for Uniswap V2", async () => {
        beforeEach(async () => {

          const leverExchangeData = defaultAbiCoder.encode(
            [ "address[]" ],
            [[setV2Setup.usdc.address, setV2Setup.wbtc.address, setV2Setup.weth.address]]
          );

          const deleverExchangeData = defaultAbiCoder.encode(
            [ "address[]" ],
            [[setV2Setup.weth.address, setV2Setup.wbtc.address, setV2Setup.usdc.address]]
          );

          const uniV2ExchangeSettings: ExchangeSettings = {
            twapMaxTradeSize: ether(100),
            incentivizedTwapMaxTradeSize: ether(100),
            exchangeLastTradeTimestamp: BigNumber.from(0),
            leverExchangeData: leverExchangeData,
            deleverExchangeData: deleverExchangeData,
          };

          await fliExtensionMock.setExchangeSettings(uniswapV2ExchangeName, uniV2ExchangeSettings);
        });

        context("when Uniswap V3 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.wbtc.address,
              ether(1000),
              bitcoin(20),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.usdc.address,
              setV2Setup.wbtc.address,
              usdc(100000),
              bitcoin(2),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(1000),
              usdc(2_000_000),
              owner.address
            );
          });

          it("should set Uniswap V3 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[1]).to.eq(1);
          });
        });

        context("when Uniswap V2 will produce a better trade", async () => {

          beforeEach(async () => {
            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.weth.address,
              setV2Setup.wbtc.address,
              ether(1000),
              bitcoin(20),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            await uniswapV2Setup.router.addLiquidity(
              setV2Setup.usdc.address,
              setV2Setup.wbtc.address,
              usdc(100000),
              bitcoin(2),
              0,
              0,
              owner.address,
              MAX_UINT_256
            );

            // very bad ETH-USDC rate
            await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
            await uniswapV3Setup.addLiquidityWide(
              setV2Setup.weth,
              setV2Setup.usdc,
              3000,
              ether(100),
              usdc(2000),
              owner.address
            );
          });

          it("should set Uniswap V2 as the preferred exchange", async () => {
            const [ exchangeNames, rebalanceEnums ] = await subject();

            expect(exchangeNames[0]).to.eq(uniswapV2ExchangeName);
            expect(rebalanceEnums[0]).to.eq(1);

            expect(exchangeNames[1]).to.eq(uniswapV3ExchangeName);
            expect(rebalanceEnums[1]).to.eq(1);
          });
        });
      });
    });

    context("when levering", async () => {

      beforeEach(async () => {
        const chunkRebalanceSizes = [ usdc(5000), usdc(3000) ];
        const chunkRebalanceSellAsset = setV2Setup.usdc.address;
        const chunkRebalanceBuyAsset = setV2Setup.weth.address;

        await fliExtensionMock.setGetChunkRebalanceWithBounds(chunkRebalanceSizes, chunkRebalanceSellAsset, chunkRebalanceBuyAsset);
      });

      context("when Uniswap V3 will produce a better trade", async () => {

        beforeEach(async () => {
          await uniswapV2Setup.router.addLiquidity(
            setV2Setup.weth.address,
            setV2Setup.usdc.address,
            ether(100),
            usdc(200_000),
            0,
            0,
            owner.address,
            MAX_UINT_256
          );

          await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
          await uniswapV3Setup.addLiquidityWide(
            setV2Setup.weth,
            setV2Setup.usdc,
            3000,
            ether(1000),
            usdc(2_000_000),
            owner.address
          );
        });

        it("should set Uniswap V3 as the preferred exchange", async () => {
          const [ exchangeNames, rebalanceEnums ] = await subject();

          expect(exchangeNames[0]).to.eq(uniswapV3ExchangeName);
          expect(rebalanceEnums[0]).to.eq(1);

          expect(exchangeNames[1]).to.eq(uniswapV2ExchangeName);
          expect(rebalanceEnums[1]).to.eq(1);
        });
      });

      context("when Uniswap V2 will produce a better trade", async () => {

        beforeEach(async () => {
          await uniswapV2Setup.router.addLiquidity(
            setV2Setup.weth.address,
            setV2Setup.usdc.address,
            ether(1000),
            usdc(2_000_000),
            0,
            0,
            owner.address,
            MAX_UINT_256
          );

          await uniswapV3Setup.createNewPair(setV2Setup.weth, setV2Setup.usdc, 3000, 2000);
          await uniswapV3Setup.addLiquidityWide(
            setV2Setup.weth,
            setV2Setup.usdc,
            3000,
            ether(100),
            usdc(200_000),
            owner.address
          );
        });

        it("should set Uniswap V2 as the preferred exchange", async () => {
          const [ exchangeNames, rebalanceEnums ] = await subject();

          expect(exchangeNames[0]).to.eq(uniswapV2ExchangeName);
          expect(rebalanceEnums[0]).to.eq(1);

          expect(exchangeNames[1]).to.eq(uniswapV3ExchangeName);
          expect(rebalanceEnums[1]).to.eq(1);
        });
      });
    });
  });
});
Example #14
Source File: NFT1155.test.ts    From shoyu with MIT License 4 votes vote down vote up
describe("NFT part of NFT1155", () => {
    beforeEach(async () => {
        await ethers.provider.send("hardhat_reset", []);
    });

    it("should be that default values are set correctly with batch minting deploy", async () => {
        const { factory, nft1155, alice, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        await factory.deployNFT1155AndMintBatch(alice.address, [0, 2, 4], [1, 3, 5], royaltyVault.address, 13);
        const nft1155_0 = await getNFT1155(factory);

        expect(await nft1155_0.PERMIT_TYPEHASH()).to.be.equal(
            convertToHash("Permit(address owner,address spender,uint256 nonce,uint256 deadline)")
        );
        expect(await nft1155_0.DOMAIN_SEPARATOR()).to.be.equal(
            await domainSeparator(ethers.provider, nft1155_0.address.toLowerCase(), nft1155_0.address)
        );
        expect(await nft1155_0.factory()).to.be.equal(factory.address);

        async function URI1155(nft: NFT1155V3, tokenId: number): Promise<string> {
            const baseURI = await factory.baseURI1155();
            const addy = nft.address.toLowerCase();
            return toUtf8String(solidityPack(["string", "string", "string"], [baseURI, addy, "/" + tokenId + ".json"]));
        }

        expect(await nft1155_0.uri(0)).to.be.equal(await URI1155(nft1155_0, 0));
        expect(await nft1155_0.uri(2)).to.be.equal(await URI1155(nft1155_0, 2));
        expect(await nft1155_0.uri(4)).to.be.equal(await URI1155(nft1155_0, 4));
        expect(await nft1155_0.uri(1)).to.be.equal(await URI1155(nft1155_0, 1));
        expect(await nft1155_0.uri(11759)).to.be.equal(await URI1155(nft1155_0, 11759));

        expect((await nft1155_0.royaltyInfo(0, 0))[0]).to.be.equal(royaltyVault.address);

        expect((await nft1155_0.royaltyInfo(0, 12345))[1]).to.be.equal(Math.floor((12345 * 13) / 1000));
    });

    it("should be that permit fuctions work well", async () => {
        const { factory, nft1155, alice, bob, carol, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        const artist = ethers.Wallet.createRandom();

        await factory.deployNFT1155AndMintBatch(artist.address, [0, 1, 2], [5, 6, 7], royaltyVault.address, 10);
        const nft1155_0 = await getNFT1155(factory);

        const currentTime = await getBlockTimestamp();
        const deadline = currentTime + 100;
        const permitDigest0 = await getDigest(
            ethers.provider,
            nft1155_0.address.toLowerCase(),
            nft1155_0.address,
            getHash(
                ["bytes32", "address", "address", "uint256", "uint256"],
                [await nft1155_0.PERMIT_TYPEHASH(), artist.address, bob.address, 0, deadline]
            )
        );
        const { v: v0, r: r0, s: s0 } = sign(permitDigest0, artist);

        expect(await nft1155_0.isApprovedForAll(artist.address, bob.address)).to.be.false;
        await nft1155_0.permit(artist.address, bob.address, deadline, v0, r0, s0);
        expect(await nft1155_0.isApprovedForAll(artist.address, bob.address)).to.be.true;

        const { v: v1, r: r1, s: s1 } = sign(
            await getDigest(
                ethers.provider,
                nft1155_0.address.toLowerCase(),
                nft1155_0.address,
                getHash(
                    ["bytes32", "address", "address", "uint256", "uint256"],
                    [await nft1155_0.PERMIT_TYPEHASH(), artist.address, alice.address, 1, deadline]
                )
            ),
            artist
        );

        const { v: fv0, r: fr0, s: fs0 } = sign(
            await getDigest(
                ethers.provider,
                nft1155_0.address.toLowerCase(),
                nft1155_0.address,
                getHash(
                    ["bytes32", "address", "address", "uint256", "uint256"],
                    [await nft1155_0.PERMIT_TYPEHASH(), artist.address, alice.address, 111, deadline] //invalid nonce
                )
            ),
            artist
        );
        const { v: fv1, r: fr1, s: fs1 } = sign(
            await getDigest(
                ethers.provider,
                nft1155_0.address.toLowerCase(),
                nft1155_0.address,
                getHash(
                    ["bytes32", "address", "address", "uint256", "uint256"],
                    [await nft1155_0.PERMIT_TYPEHASH(), artist.address, alice.address, 3, deadline - 120] //deadline over
                )
            ),
            artist
        );
        const fakeSigner = ethers.Wallet.createRandom();
        const { v: fv2, r: fr2, s: fs2 } = sign(
            await getDigest(
                ethers.provider,
                nft1155_0.address.toLowerCase(),
                nft1155_0.address,
                getHash(
                    ["bytes32", "address", "address", "uint256", "uint256"],
                    [await nft1155_0.PERMIT_TYPEHASH(), artist.address, alice.address, 3, deadline] //fake signer
                )
            ),
            fakeSigner
        );

        await expect(nft1155_0.permit(artist.address, alice.address, deadline, fv0, fr0, fs0)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        ); //invalid nonce
        await expect(nft1155_0.permit(artist.address, alice.address, deadline - 120, fv1, fr1, fs1)).to.be.revertedWith(
            "SHOYU: EXPIRED"
        ); //deadline over
        await expect(nft1155_0.permit(artist.address, carol.address, deadline, v1, r1, s1)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        ); //wrong spender
        await expect(nft1155_0.permit(artist.address, alice.address, deadline, fv2, fr2, fs2)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        ); //fake signer

        await nft1155_0.permit(artist.address, alice.address, deadline, v1, r1, s1); //wrong id
        expect(await nft1155_0.isApprovedForAll(artist.address, alice.address)).to.be.true;
    });

    it("should be that owner can only decrease royalty fee", async () => {
        const { factory, nft1155, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        await factory.deployNFT1155AndMintBatch(alice.address, [7], [10], royaltyVault.address, 20);
        const nft1155_0 = await getNFT1155(factory);

        await expect(nft1155_0.setRoyaltyFee(10)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft1155_0.connect(alice).setRoyaltyFee(30)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft1155_0.connect(alice).setRoyaltyFee(3);
        expect((await nft1155_0.royaltyInfo(0, 1000))[1]).to.be.equal(3);
        await nft1155_0.connect(alice).setRoyaltyFee(0);
        expect((await nft1155_0.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft1155_0.connect(alice).setRoyaltyFee(1)).to.be.revertedWith("SHOYU: INVALID_FEE");

        await factory.deployNFT1155AndMintBatch(bob.address, [9, 11], [10, 50], royaltyVault.address, 0);
        const nft1155_1 = await getNFT1155(factory);
        expect((await nft1155_1.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft1155_1.connect(bob).setRoyaltyFee(251)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft1155_1.connect(bob).setRoyaltyFee(93);
        expect((await nft1155_1.royaltyInfo(0, 1000))[1]).to.be.equal(93);
        await expect(nft1155_1.connect(bob).setRoyaltyFee(111)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft1155_1.connect(bob).setRoyaltyFee(0);
        expect((await nft1155_1.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft1155_1.connect(bob).setRoyaltyFee(1)).to.be.revertedWith("SHOYU: INVALID_FEE");
    });

    it("should be that URI functions work well", async () => {
        const { factory, nft1155, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        async function URI1155(nft: NFT1155V3, tokenId: number, _baseURI?: string): Promise<string> {
            if (_baseURI === undefined) {
                const baseURI = await factory.baseURI1155();
                const addy = nft.address.toLowerCase();
                return toUtf8String(
                    solidityPack(["string", "string", "string"], [baseURI, addy, "/" + tokenId + ".json"])
                );
            } else {
                return toUtf8String(solidityPack(["string", "string"], [_baseURI, tokenId + ".json"]));
            }
        }

        await factory.deployNFT1155AndMintBatch(alice.address, [10], [3], royaltyVault.address, 10);
        const nft1155_0 = await getNFT1155(factory);

        await expect(nft1155_0.connect(bob).setURI(0, "https://foo.bar/0.json")).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft1155_0.connect(alice).setURI(0, "https://foo.bar/0.json");
        await nft1155_0.connect(alice).setURI(1, "https://foo.bar/1.json");

        expect(await nft1155_0.uri(0)).to.be.equal("https://foo.bar/0.json");
        expect(await nft1155_0.uri(1)).to.be.equal("https://foo.bar/1.json");

        expect(await nft1155_0.uri(2)).to.be.equal(await URI1155(nft1155_0, 2));
        expect(await nft1155_0.uri(4)).to.be.equal(await URI1155(nft1155_0, 4));
        expect(await nft1155_0.uri(7)).to.be.equal(await URI1155(nft1155_0, 7));
        expect(await nft1155_0.uri(2)).to.be.not.equal(await URI1155(nft1155_0, 2, "https://foo.bar/"));
        expect(await nft1155_0.uri(4)).to.be.not.equal(await URI1155(nft1155_0, 4, "https://foo.bar/"));
        expect(await nft1155_0.uri(7)).to.be.not.equal(await URI1155(nft1155_0, 7, "https://foo.bar/"));

        await expect(nft1155_0.connect(bob).setBaseURI("https://foo.bar/")).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft1155_0.connect(alice).setBaseURI("https://foo.bar/");

        expect(await nft1155_0.uri(2)).to.be.equal(await URI1155(nft1155_0, 2, "https://foo.bar/"));
        expect(await nft1155_0.uri(4)).to.be.equal(await URI1155(nft1155_0, 4, "https://foo.bar/"));
        expect(await nft1155_0.uri(7)).to.be.equal(await URI1155(nft1155_0, 7, "https://foo.bar/"));
        expect(await nft1155_0.uri(2)).to.be.not.equal(await URI1155(nft1155_0, 2));
        expect(await nft1155_0.uri(4)).to.be.not.equal(await URI1155(nft1155_0, 4));
        expect(await nft1155_0.uri(7)).to.be.not.equal(await URI1155(nft1155_0, 7));
    });

    it("should be that mint/mintBatch functions work well", async () => {
        const { factory, nft1155, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        await factory.deployNFT1155AndMintBatch(bob.address, [0, 2, 4], [5, 6, 7], royaltyVault.address, 10);
        const nft1155_0 = await getNFT1155(factory);

        await expect(nft1155_0.mint(alice.address, 1, 1, [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft1155_0.connect(bob).mint(AddressZero, 1, 1, [])).to.be.revertedWith("SHOYU: INVALID_ADDRESS");
        await expect(nft1155_0.connect(bob).mint(factory.address, 1, 1, [])).to.be.revertedWith("SHOYU: NO_RECEIVER");

        await nft1155_0.connect(bob).mint(alice.address, 0, 100, []);
        await nft1155_0.connect(bob).mint(alice.address, 1, 100, []);

        await expect(nft1155_0.mintBatch(alice.address, [3, 5], [30, 50], [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft1155_0.connect(bob).mintBatch(AddressZero, [3, 5], [33, 55], [])).to.be.revertedWith(
            "SHOYU: INVALID_ADDRESS"
        );

        await expect(nft1155_0.connect(bob).mintBatch(factory.address, [3, 5], [33, 55], [])).to.be.revertedWith(
            "SHOYU: NO_RECEIVER"
        );

        await nft1155_0.connect(bob).mintBatch(alice.address, [3, 5], [33, 55], []);
        await nft1155_0.connect(bob).mint(alice.address, [3, 5], [1, 3], []);
    });

    it("should be that burn/burnBatch functions work well", async () => {
        const { factory, nft1155, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        await factory.deployNFT1155AndMintBatch(bob.address, [1, 2, 4], [10, 20, 40], royaltyVault.address, 10);
        const nft1155_0 = await getNFT1155(factory);
        await nft1155_0.connect(bob).safeBatchTransferFrom(bob.address, alice.address, [1, 2, 4], [1, 2, 4], []);

        await expect(nft1155_0.connect(bob).burn(6, 1, 0, HashZero)).to.be.revertedWith("SHOYU: INSUFFICIENT_BALANCE");
        await expect(nft1155_0.connect(alice).burn(4, 5, 0, HashZero)).to.be.revertedWith(
            "SHOYU: INSUFFICIENT_BALANCE"
        );
        await nft1155_0.connect(bob).burn(4, 33, 0, HashZero);
        await expect(nft1155_0.connect(bob).burn(4, 33, 0, HashZero)).to.be.revertedWith("SHOYU: INSUFFICIENT_BALANCE");

        await nft1155_0.connect(alice).burn(4, 4, 0, HashZero);
        //alice : 1-1 2-2 4-0  _  bob : 1-9 2-18 4-3

        await expect(nft1155_0.connect(bob).burnBatch([2, 4], [10, 4])).to.be.revertedWith(
            "SHOYU: INSUFFICIENT_BALANCE"
        );
        await expect(nft1155_0.connect(bob).burnBatch([2, 8], [10, 1])).to.be.revertedWith(
            "SHOYU: INSUFFICIENT_BALANCE"
        );
        await expect(nft1155_0.connect(bob).burnBatch([4, 8], [1, 4])).to.be.revertedWith(
            "SHOYU: INSUFFICIENT_BALANCE"
        );
        await expect(nft1155_0.connect(bob).burnBatch([5, 10], [1, 1])).to.be.revertedWith(
            "SHOYU: INSUFFICIENT_BALANCE"
        );

        await nft1155_0.connect(alice).burnBatch([1, 2], [1, 2]);
        await nft1155_0.connect(bob).burnBatch([1, 2, 4], [9, 18, 3]);
    });

    it("should be that static call functions work properly", async () => {
        const { factory, nft1155, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT1155(nft1155.address);

        await factory.deployNFT1155AndMintBatch(bob.address, [1, 2, 4], [10, 20, 40], royaltyVault.address, 10);
        const nft1155_0 = await getNFT1155(factory);

        expect(await nft1155_0.target()).to.be.equal(AddressZero);
        
        const StaticCallMock = await ethers.getContractFactory("StaticCallMock");
        const target0 = (await StaticCallMock.deploy()) as StaticCallMock;

        let proxyNFT = (await StaticCallMock.attach(nft1155_0.address)) as StaticCallMock;
        await expect(proxyNFT.globalV()).to.be.reverted;
        await expect(proxyNFT.pureTest11(1)).to.be.reverted;

        await expect(nft1155_0.setTarget(target0.address)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft1155_0.connect(bob).setTarget(target0.address);
        expect(await nft1155_0.target()).to.be.equal(target0.address);

        expect(await proxyNFT.globalV()).to.be.equal(target0.address);
        expect(await proxyNFT.pureTest11(1)).to.be.equal(1);
        expect((await proxyNFT.pureTest23(2, "abc"))[0]).to.be.equal(2);
        expect((await proxyNFT.pureTest23(2, "abc"))[1]).to.be.equal("0x0000000000000000000000000000000000000001");
        expect((await proxyNFT.pureTest23(2, "abc"))[2]).to.be.equal("abc");

        expect(await proxyNFT.viewTest11(10)).to.be.equal(0);
        await target0.setX(10, 123);
        expect(await proxyNFT.viewTest11(10)).to.be.equal(123);

        await target0.setY(11, "qwe");
        expect((await proxyNFT.viewTest13(11))[0]).to.be.equal(0);
        expect((await proxyNFT.viewTest13(11))[1]).to.be.equal(target0.address);
        expect((await proxyNFT.viewTest13(11))[2]).to.be.equal("qwe");

        await target0.setX(11, 77);
        await target0.setY(11, "zxc");
        expect((await proxyNFT.viewTest13(11))[0]).to.be.equal(77);
        expect((await proxyNFT.viewTest13(11))[1]).to.be.equal(target0.address);
        expect((await proxyNFT.viewTest13(11))[2]).to.be.equal("zxc");
    });
});
Example #15
Source File: NFT721.test.ts    From shoyu with MIT License 4 votes vote down vote up
describe("NFT part of NFT721", () => {
    beforeEach(async () => {
        await ethers.provider.send("hardhat_reset", []);
    });

    it("should be that default values are set correctly with batch minting deploy", async () => {
        const { factory, nft721, alice, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndMintBatch(alice.address, "Name", "Symbol", [0, 2, 4], royaltyVault.address, 13);
        const nft721_0 = await getNFT721(factory);

        expect(await nft721_0.PERMIT_TYPEHASH()).to.be.equal(
            convertToHash("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)")
        );
        expect(await nft721_0.PERMIT_ALL_TYPEHASH()).to.be.equal(
            convertToHash("Permit(address owner,address spender,uint256 nonce,uint256 deadline)")
        );
        expect(await nft721_0.DOMAIN_SEPARATOR()).to.be.equal(
            await domainSeparator(ethers.provider, "Name", nft721_0.address)
        );
        expect(await nft721_0.factory()).to.be.equal(factory.address);

        async function URI721(nft: NFT721V2, tokenId: number): Promise<string> {
            const baseURI = await factory.baseURI721();
            const addy = nft.address.toLowerCase();
            return toUtf8String(
                solidityPack(
                    ["string", "string", "string", "string", "string"],
                    [baseURI, addy, "/", tokenId.toString(), ".json"]
                )
            );
        }

        expect(await nft721_0.tokenURI(0)).to.be.equal(await URI721(nft721_0, 0));
        expect(await nft721_0.tokenURI(2)).to.be.equal(await URI721(nft721_0, 2));
        expect(await nft721_0.tokenURI(4)).to.be.equal(await URI721(nft721_0, 4));
        await expect(nft721_0.tokenURI(1)).to.be.revertedWith("SHOYU: INVALID_TOKEN_ID");

        expect((await nft721_0.royaltyInfo(0, 0))[0]).to.be.equal(royaltyVault.address);

        expect((await nft721_0.royaltyInfo(0, 12345))[1]).to.be.equal(Math.floor((12345 * 13) / 1000));

        for (let i = 0; i < 10; i++) {
            assert.isFalse(await nft721_0.parked(i));
        }
    });

    it("should be that default values are set correctly with parking deploy", async () => {
        const { factory, nft721, alice, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndPark(alice.address, "Name", "Symbol", 7, royaltyVault.address, 13);
        const nft721_0 = await getNFT721(factory);

        expect(await nft721_0.PERMIT_TYPEHASH()).to.be.equal(
            convertToHash("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)")
        );
        expect(await nft721_0.PERMIT_ALL_TYPEHASH()).to.be.equal(
            convertToHash("Permit(address owner,address spender,uint256 nonce,uint256 deadline)")
        );
        expect(await nft721_0.DOMAIN_SEPARATOR()).to.be.equal(
            await domainSeparator(ethers.provider, "Name", nft721_0.address)
        );
        expect(await nft721_0.factory()).to.be.equal(factory.address);

        async function URI721(nft: NFT721V2, tokenId: number): Promise<string> {
            const baseURI = await factory.baseURI721();
            const addy = nft.address.toLowerCase();
            return toUtf8String(
                solidityPack(
                    ["string", "string", "string", "string", "string"],
                    [baseURI, addy, "/", tokenId.toString(), ".json"]
                )
            );
        }

        expect(await nft721_0.tokenURI(0)).to.be.equal(await URI721(nft721_0, 0));
        expect(await nft721_0.tokenURI(3)).to.be.equal(await URI721(nft721_0, 3));
        expect(await nft721_0.tokenURI(6)).to.be.equal(await URI721(nft721_0, 6));
        await expect(nft721_0.tokenURI(7)).to.be.revertedWith("SHOYU: INVALID_TOKEN_ID");

        expect((await nft721_0.royaltyInfo(0, 0))[0]).to.be.equal(royaltyVault.address);

        expect((await nft721_0.royaltyInfo(0, 12345))[1]).to.be.equal(Math.floor((12345 * 13) / 1000));

        for (let i = 0; i <= 6; i++) {
            assert.isTrue(await nft721_0.parked(i));
        }
        assert.isFalse(await nft721_0.parked(7));
        assert.isFalse(await nft721_0.parked(8));
        assert.isFalse(await nft721_0.parked(9));
        assert.isFalse(await nft721_0.parked(10));
    });

    it("should be that permit/permitAll fuctions work well", async () => {
        const { factory, nft721, alice, bob, carol, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        const artist = ethers.Wallet.createRandom();

        await factory.deployNFT721AndMintBatch(artist.address, "Name", "Symbol", [0, 1, 2], royaltyVault.address, 10);
        const nft721_0 = await getNFT721(factory);

        const currentTime = await getBlockTimestamp();
        let deadline = currentTime + 100;
        const permitDigest0 = await getDigest(
            ethers.provider,
            "Name",
            nft721_0.address,
            getHash(
                ["bytes32", "address", "uint256", "uint256", "uint256"],
                [await nft721_0.PERMIT_TYPEHASH(), bob.address, 1, 0, deadline]
            )
        );
        const { v: v0, r: r0, s: s0 } = sign(permitDigest0, artist);

        expect(await nft721_0.getApproved(1)).to.be.equal(AddressZero);
        await nft721_0.permit(bob.address, 1, deadline, v0, r0, s0);
        expect(await nft721_0.getApproved(1)).to.be.equal(bob.address);

        const { v: v1, r: r1, s: s1 } = sign(
            await getDigest(
                ethers.provider,
                "Name",
                nft721_0.address,
                getHash(
                    ["bytes32", "address", "uint256", "uint256", "uint256"],
                    [await nft721_0.PERMIT_TYPEHASH(), bob.address, 2, 1, deadline]
                )
            ),
            artist
        );

        const { v: fv0, r: fr0, s: fs0 } = sign(
            await getDigest(
                ethers.provider,
                "Name",
                nft721_0.address,
                getHash(
                    ["bytes32", "address", "uint256", "uint256", "uint256"],
                    [await nft721_0.PERMIT_TYPEHASH(), bob.address, 2, 5, deadline] //invalid nonce
                )
            ),
            artist
        );
        const { v: fv1, r: fr1, s: fs1 } = sign(
            await getDigest(
                ethers.provider,
                "Name",
                nft721_0.address,
                getHash(
                    ["bytes32", "address", "uint256", "uint256", "uint256"],
                    [await nft721_0.PERMIT_TYPEHASH(), bob.address, 2, 1, deadline - 120] //deadline over
                )
            ),
            artist
        );
        const fakeSigner = ethers.Wallet.createRandom();
        const { v: fv2, r: fr2, s: fs2 } = sign(
            await getDigest(
                ethers.provider,
                "Name",
                nft721_0.address,
                getHash(
                    ["bytes32", "address", "uint256", "uint256", "uint256"],
                    [await nft721_0.PERMIT_TYPEHASH(), bob.address, 2, 1, deadline] //fake signer
                )
            ),
            fakeSigner
        );

        await expect(nft721_0.permit(bob.address, 2, deadline, fv0, fr0, fs0)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        ); //invalid nonce
        await expect(nft721_0.permit(bob.address, 2, deadline - 150, fv1, fr1, fs1)).to.be.revertedWith(
            "SHOYU: EXPIRED"
        ); //deadline over
        await expect(nft721_0.permit(bob.address, 5, deadline, v1, r1, s1)).to.be.revertedWith(
            "SHOYU: INVALID_TOKENID"
        ); //wrong id
        await expect(nft721_0.permit(carol.address, 2, deadline, v1, r1, s1)).to.be.revertedWith("SHOYU: UNAUTHORIZED"); //wrong spender
        await expect(nft721_0.permit(bob.address, 2, deadline, fv2, fr2, fs2)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        ); //fake signer

        const permitAllDigest0 = await getDigest(
            ethers.provider,
            "Name",
            nft721_0.address,
            getHash(
                ["bytes32", "address", "address", "uint256", "uint256"],
                [await nft721_0.PERMIT_ALL_TYPEHASH(), artist.address, carol.address, 0, deadline]
            )
        );
        const { v: v2, r: r2, s: s2 } = sign(permitAllDigest0, artist);

        expect(await nft721_0.isApprovedForAll(artist.address, carol.address)).to.be.false;

        await expect(nft721_0.permitAll(artist.address, alice.address, deadline, v2, r2, s2)).to.be.revertedWith(
            "SHOYU: UNAUTHORIZED"
        );
        await nft721_0.permitAll(artist.address, carol.address, deadline, v2, r2, s2);

        expect(await nft721_0.isApprovedForAll(artist.address, carol.address)).to.be.true;
    });

    it("should be that owner can only decrease royalty fee", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndPark(alice.address, "Name", "Symbol", 7, royaltyVault.address, 20);
        const nft721_0 = await getNFT721(factory);

        await expect(nft721_0.setRoyaltyFee(10)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(alice).setRoyaltyFee(30)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft721_0.connect(alice).setRoyaltyFee(3);
        expect((await nft721_0.royaltyInfo(0, 1000))[1]).to.be.equal(3);
        await nft721_0.connect(alice).setRoyaltyFee(0);
        expect((await nft721_0.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft721_0.connect(alice).setRoyaltyFee(1)).to.be.revertedWith("SHOYU: INVALID_FEE");

        await factory.deployNFT721AndMintBatch(bob.address, "Name", "Symbol", [9, 11], royaltyVault.address, 0);
        const nft721_1 = await getNFT721(factory);
        expect((await nft721_1.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft721_1.connect(bob).setRoyaltyFee(251)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft721_1.connect(bob).setRoyaltyFee(93);
        expect((await nft721_1.royaltyInfo(0, 1000))[1]).to.be.equal(93);
        await expect(nft721_1.connect(bob).setRoyaltyFee(111)).to.be.revertedWith("SHOYU: INVALID_FEE");
        await nft721_1.connect(bob).setRoyaltyFee(0);
        expect((await nft721_1.royaltyInfo(0, 1000))[1]).to.be.equal(0);
        await expect(nft721_1.connect(bob).setRoyaltyFee(1)).to.be.revertedWith("SHOYU: INVALID_FEE");
    });

    it("should be that URI functions work well", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        async function URI721(nft: NFT721V2, tokenId: number, _baseURI?: string): Promise<string> {
            if (_baseURI === undefined) {
                const baseURI = await factory.baseURI721();
                const addy = nft.address.toLowerCase();
                return toUtf8String(
                    solidityPack(
                        ["string", "string", "string", "string", "string"],
                        [baseURI, addy, "/", tokenId.toString(), ".json"]
                    )
                );
            } else {
                return toUtf8String(
                    solidityPack(["string", "string", "string"], [_baseURI, tokenId.toString(), ".json"])
                );
            }
        }

        await factory.deployNFT721AndPark(alice.address, "Name", "Symbol", 10, royaltyVault.address, 10);
        const nft721_0 = await getNFT721(factory);

        await expect(nft721_0.connect(bob).setTokenURI(0, "https://foo.bar/0.json")).to.be.revertedWith(
            "SHOYU: FORBIDDEN"
        );
        await nft721_0.connect(alice).setTokenURI(0, "https://foo.bar/0.json");
        await nft721_0.connect(alice).setTokenURI(1, "https://foo.bar/1.json");

        expect(await nft721_0.tokenURI(0)).to.be.equal("https://foo.bar/0.json");
        expect(await nft721_0.tokenURI(1)).to.be.equal("https://foo.bar/1.json");

        expect(await nft721_0.tokenURI(2)).to.be.equal(await URI721(nft721_0, 2));
        expect(await nft721_0.tokenURI(4)).to.be.equal(await URI721(nft721_0, 4));
        expect(await nft721_0.tokenURI(7)).to.be.equal(await URI721(nft721_0, 7));
        expect(await nft721_0.tokenURI(2)).to.be.not.equal(await URI721(nft721_0, 2, "https://foo.bar/"));
        expect(await nft721_0.tokenURI(4)).to.be.not.equal(await URI721(nft721_0, 4, "https://foo.bar/"));
        expect(await nft721_0.tokenURI(7)).to.be.not.equal(await URI721(nft721_0, 7, "https://foo.bar/"));

        await expect(nft721_0.connect(bob).setBaseURI("https://foo.bar/")).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft721_0.connect(alice).setBaseURI("https://foo.bar/");

        expect(await nft721_0.tokenURI(2)).to.be.equal(await URI721(nft721_0, 2, "https://foo.bar/"));
        expect(await nft721_0.tokenURI(4)).to.be.equal(await URI721(nft721_0, 4, "https://foo.bar/"));
        expect(await nft721_0.tokenURI(7)).to.be.equal(await URI721(nft721_0, 7, "https://foo.bar/"));
        expect(await nft721_0.tokenURI(2)).to.be.not.equal(await URI721(nft721_0, 2));
        expect(await nft721_0.tokenURI(4)).to.be.not.equal(await URI721(nft721_0, 4));
        expect(await nft721_0.tokenURI(7)).to.be.not.equal(await URI721(nft721_0, 7));
    });

    it("should be that parkTokenIds func work well", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndPark(alice.address, "Name", "Symbol", 50, royaltyVault.address, 10);
        const nft721_0 = await getNFT721(factory);

        await expect(nft721_0.connect(bob).parkTokenIds(100)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(alice).parkTokenIds(30)).to.be.revertedWith("SHOYU: INVALID_TO_TOKEN_ID");
        await expect(nft721_0.connect(alice).parkTokenIds(50)).to.be.revertedWith("SHOYU: INVALID_TO_TOKEN_ID");
        await nft721_0.connect(alice).parkTokenIds(51);
        await nft721_0.connect(alice).parkTokenIds(100);
    });

    it("should be that mint/mintBatch functions work well", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndMintBatch(bob.address, "Name", "Symbol", [0, 2, 4], royaltyVault.address, 10);
        const nft721_0 = await getNFT721(factory);

        await expect(nft721_0.mint(alice.address, 1, [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(bob).mint(alice.address, 0, [])).to.be.revertedWith("SHOYU: ALREADY_MINTED");
        await expect(nft721_0.connect(bob).mint(AddressZero, 1, [])).to.be.revertedWith("SHOYU: INVALID_TO");
        await expect(nft721_0.connect(bob).mint(factory.address, 1, [])).to.be.revertedWith("SHOYU: INVALID_RECEIVER");

        await nft721_0.connect(bob).mint(alice.address, 1, []); //0,1,2,4 are minted

        await expect(nft721_0.mintBatch(alice.address, [3, 5], [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(bob).mintBatch(alice.address, [3, 4], [])).to.be.revertedWith(
            "SHOYU: ALREADY_MINTED"
        );
        await expect(nft721_0.connect(bob).mintBatch(AddressZero, [3, 5], [])).to.be.revertedWith("SHOYU: INVALID_TO");
        await expect(nft721_0.connect(bob).mintBatch(factory.address, [3, 5], [])).to.be.revertedWith(
            "SHOYU: INVALID_RECEIVER"
        );

        await nft721_0.connect(bob).mint(alice.address, [3, 5], []); //0,1,2,3,4,5 are minted

        await factory.deployNFT721AndPark(alice.address, "Name", "Symbol", 50, royaltyVault.address, 10);
        const nft721_1 = await getNFT721(factory); //nothing is minted. 0-49 are parked

        await expect(nft721_1.mint(bob.address, 1, [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_1.connect(alice).mint(AddressZero, 1, [])).to.be.revertedWith("SHOYU: INVALID_TO");
        await expect(nft721_1.connect(alice).mint(factory.address, 1, [])).to.be.revertedWith(
            "SHOYU: INVALID_RECEIVER"
        );

        await nft721_1.connect(alice).mint(bob.address, 1, []);
        await nft721_1.connect(alice).mint(bob.address, 50, []); //1,50 are minted. 0-49 are parked

        await expect(nft721_1.mintBatch(bob.address, [3, 5, 7], [])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_1.connect(alice).mintBatch(bob.address, [1, 5, 7], [])).to.be.revertedWith(
            "SHOYU: ALREADY_MINTED"
        );
        await expect(nft721_1.connect(alice).mintBatch(AddressZero, [3, 5, 7], [])).to.be.revertedWith(
            "SHOYU: INVALID_TO"
        );
        await expect(nft721_1.connect(alice).mintBatch(factory.address, [3, 5, 7], [])).to.be.revertedWith(
            "SHOYU: INVALID_RECEIVER"
        );

        await nft721_1.connect(alice).mint(bob.address, [3, 5, 7], []); //1,3,5,7,50 are minted. 0-49 are parked
        await nft721_1.connect(alice).mint(bob.address, [40, 55], []); //1,3,5,7,40,50,55 are minted. 0-49 are parked
        await nft721_1.connect(alice).mint(bob.address, [80, 100], []); //1,3,5,7,40,50,55,80,100 are minted. 0-49 are parked
    });

    it("should be that burn/burnBatch functions work well", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndMintBatch(
            bob.address,
            "Name",
            "Symbol",
            [0, 2, 4, 6, 8, 10],
            royaltyVault.address,
            10
        );
        const nft721_0 = await getNFT721(factory);
        await nft721_0.connect(bob).transferFrom(bob.address, alice.address, 6);
        await nft721_0.connect(bob).transferFrom(bob.address, alice.address, 8);
        await nft721_0.connect(bob).transferFrom(bob.address, alice.address, 10);
        //bob : owner & 0,2,4 _  alice : notOwner & 6,8,10

        await expect(nft721_0.connect(bob).burn(6, 0, HashZero)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(alice).burn(4, 0, HashZero)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft721_0.connect(bob).burn(0, 0, HashZero); //0 is burned

        await expect(nft721_0.connect(bob).burn(0, 0, HashZero)).to.be.revertedWith("SHOYU: FORBIDDEN");

        await nft721_0.connect(alice).burn(6, 0, HashZero); //0,6 is burned

        //bob : owner & 2,4 _  alice : notOwner & 8,10
        await expect(nft721_0.connect(bob).burnBatch([2, 3])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(bob).burnBatch([2, 8])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(bob).burnBatch([3, 8])).to.be.revertedWith("SHOYU: FORBIDDEN");
        await expect(nft721_0.connect(bob).burnBatch([8, 10])).to.be.revertedWith("SHOYU: FORBIDDEN");

        await nft721_0.connect(alice).burnBatch([8, 10]);
        await nft721_0.connect(bob).burnBatch([2]);
    });

    it("should be that static call functions work properly", async () => {
        const { factory, nft721, alice, bob, royaltyVault } = await setupTest();

        await factory.setDeployerWhitelisted(AddressZero, true);
        await factory.upgradeNFT721(nft721.address);

        await factory.deployNFT721AndMintBatch(
            bob.address,
            "Name",
            "Symbol",
            [0, 2, 4, 6, 8, 10],
            royaltyVault.address,
            10
        );
        const nft721_0 = await getNFT721(factory);

        expect(await nft721_0.target()).to.be.equal(AddressZero);
        
        const StaticCallMock = await ethers.getContractFactory("StaticCallMock");
        const target0 = (await StaticCallMock.deploy()) as StaticCallMock;

        let proxyNFT = (await StaticCallMock.attach(nft721_0.address)) as StaticCallMock;
        await expect(proxyNFT.globalV()).to.be.reverted;
        await expect(proxyNFT.pureTest11(1)).to.be.reverted;

        await expect(nft721_0.setTarget(target0.address)).to.be.revertedWith("SHOYU: FORBIDDEN");
        await nft721_0.connect(bob).setTarget(target0.address);
        expect(await nft721_0.target()).to.be.equal(target0.address);

        expect(await proxyNFT.globalV()).to.be.equal(target0.address);
        expect(await proxyNFT.pureTest11(1)).to.be.equal(1);
        expect((await proxyNFT.pureTest23(2, "abc"))[0]).to.be.equal(2);
        expect((await proxyNFT.pureTest23(2, "abc"))[1]).to.be.equal("0x0000000000000000000000000000000000000001");
        expect((await proxyNFT.pureTest23(2, "abc"))[2]).to.be.equal("abc");

        expect(await proxyNFT.viewTest11(10)).to.be.equal(0);
        await target0.setX(10, 123);
        expect(await proxyNFT.viewTest11(10)).to.be.equal(123);

        await target0.setY(11, "qwe");
        expect((await proxyNFT.viewTest13(11))[0]).to.be.equal(0);
        expect((await proxyNFT.viewTest13(11))[1]).to.be.equal(target0.address);
        expect((await proxyNFT.viewTest13(11))[2]).to.be.equal("qwe");

        await target0.setX(11, 77);
        await target0.setY(11, "zxc");
        expect((await proxyNFT.viewTest13(11))[0]).to.be.equal(77);
        expect((await proxyNFT.viewTest13(11))[1]).to.be.equal(target0.address);
        expect((await proxyNFT.viewTest13(11))[2]).to.be.equal("zxc");
    });
});