diff --git a/lib/claim.ts b/lib/claim.ts index a5df4ba..7ffd236 100644 --- a/lib/claim.ts +++ b/lib/claim.ts @@ -1,17 +1,5 @@ -import { - Account, - CallData, - Contract, - RPC, - TransactionReceipt, - Uint256, - hash, - num, - shortString, - uint256, -} from "starknet"; -import { LegacyStarknetKeyPair, deployer, manager } from "."; -import { GIFT_AMOUNT, GIFT_MAX_FEE } from "./deposit"; +import { Account, RPC, TransactionReceipt, UniversalDetails, ec, encode, num, shortString, uint256 } from "starknet"; +import { LegacyStarknetKeyPair, calculateClaimAddress, deployer, ethAddress, manager, strkAddress } from "."; const typesRev1 = { StarknetDomain: [ @@ -50,7 +38,7 @@ export async function getClaimExternalData(claimExternal: ClaimExternal) { export interface AccountConstructorArguments { sender: string; - amount: Uint256; + amount: bigint; max_fee: bigint; token: string; claim_pubkey: bigint; @@ -61,79 +49,65 @@ export interface Claim extends AccountConstructorArguments { class_hash: string; } -export function buildClaim( - factory: Contract, - claimAccountClassHash: string, - giftAmount: bigint, - giftMaxFee: bigint, - tokenContract: Contract, - claimPubkey: bigint, -): Claim { - const constructorArgs: AccountConstructorArguments = { - sender: deployer.address, - amount: uint256.bnToUint256(giftAmount), - max_fee: giftMaxFee, - token: tokenContract.address, - claim_pubkey: claimPubkey, - }; +export function buildCallDataClaim(claim: Claim) { return { - factory: factory.address, - class_hash: claimAccountClassHash, - ...constructorArgs, + ...claim, + amount: uint256.bnToUint256(claim.amount), }; } export async function claimExternal( - factory: Contract, - receiver: string, claim: Claim, + receiver: string, giftPrivateKey: string, account = deployer, ): Promise { - const constructorArgs: AccountConstructorArguments = { - sender: deployer.address, - amount: claim.amount, - max_fee: claim.max_fee, - token: claim.token, - claim_pubkey: claim.claim_pubkey, - }; - const claimAccountAddress = hash.calculateContractAddressFromHash( - 0, - claim.class_hash, - CallData.compile({ constructorArgs }), - factory.address, - ); - + const claimAddress = calculateClaimAddress(claim); const giftSigner = new LegacyStarknetKeyPair(giftPrivateKey); const claimExternalData = await getClaimExternalData({ receiver }); - const signature = await giftSigner.signMessage(claimExternalData, claimAccountAddress); + const signature = await giftSigner.signMessage(claimExternalData, claimAddress); - factory.connect(account); - return await factory.claim_external(claim, receiver, signature); + return (await account.execute([ + { + contractAddress: claim.factory, + calldata: [buildCallDataClaim(claim), receiver, signature], + entrypoint: "claim_external", + }, + ])) as TransactionReceipt; } export async function claimInternal( - factory: Contract, - tokenContract: Contract, + claim: Claim, + receiver: string, claimSignerPrivateKey: string, - claimSignerPublicKey: bigint, - receiver = "0x42", - useTxV3 = false, - giftAmount = GIFT_AMOUNT, - giftMaxFee = GIFT_MAX_FEE, + details?: UniversalDetails, ): Promise { - const claimAccountClassHash = await manager.declareLocalContract("ClaimAccount"); - const claimAddress = await factory.get_claim_address( - claimAccountClassHash, - deployer.address, - giftAmount, - giftMaxFee, - tokenContract.address, - claimSignerPublicKey, - ); - const claim = buildClaim(factory, claimAccountClassHash, giftAmount, giftMaxFee, tokenContract, claimSignerPublicKey); - const txVersion = useTxV3 ? RPC.ETransactionVersion.V3 : RPC.ETransactionVersion.V2; + const claimAddress = calculateClaimAddress(claim); + + const txVersion = useTxv3(claim.token) ? RPC.ETransactionVersion.V3 : RPC.ETransactionVersion.V2; const claimAccount = new Account(manager, num.toHex(claimAddress), claimSignerPrivateKey, undefined, txVersion); - factory.connect(claimAccount); - return await factory.claim_internal(claim, receiver); + return (await claimAccount.execute( + [ + { + contractAddress: claim.factory, + calldata: [buildCallDataClaim(claim), receiver], + entrypoint: "claim_internal", + }, + ], + undefined, + { ...details }, + )) as TransactionReceipt; +} + +export const randomReceiver = (): string => { + return `0x${encode.buf2hex(ec.starkCurve.utils.randomPrivateKey())}`; +}; + +export function useTxv3(tokenAddress: string): boolean { + if (tokenAddress === ethAddress) { + return false; + } else if (tokenAddress === strkAddress) { + return true; + } + throw new Error(`Unsupported token`); } diff --git a/lib/deposit.ts b/lib/deposit.ts index 2d5915b..af8d2e5 100644 --- a/lib/deposit.ts +++ b/lib/deposit.ts @@ -1,5 +1,5 @@ -import { CallData, Contract, hash, uint256 } from "starknet"; -import { AccountConstructorArguments, LegacyStarknetKeyPair, deployer, manager } from "./"; +import { CallData, Contract, ec, encode, hash, uint256 } from "starknet"; +import { AccountConstructorArguments, Claim, LegacyStarknetKeyPair, deployer, manager } from "./"; export const GIFT_AMOUNT = 1000000000000000n; export const GIFT_MAX_FEE = 50000000000000n; @@ -28,33 +28,46 @@ export async function defaultDepositTestSetup( giftAmount = GIFT_AMOUNT, giftMaxFee = GIFT_MAX_FEE, ): Promise<{ - claimAddress: string; - tokenContract: Contract; - claimSigner: LegacyStarknetKeyPair; + claim: Claim; + claimPrivateKey: string; }> { const tokenContract = await manager.tokens.feeTokenContract(useTxV3); // static signer for gas profiling - const claimSigner = new LegacyStarknetKeyPair(giftPrivateKey || "0x42"); + const claimSigner = new LegacyStarknetKeyPair( + giftPrivateKey || `0x${encode.buf2hex(ec.starkCurve.utils.randomPrivateKey())}`, + ); const claimPubKey = claimSigner.publicKey; await deposit(factory, tokenContract, claimPubKey); - const claimAccountClassHash = await manager.declareLocalContract("ClaimAccount"); + const claimClassHash = await factory.get_latest_claim_class_hash(); - const constructorArgs: AccountConstructorArguments = { + const claim: Claim = { + factory: factory.address, + class_hash: claimClassHash, sender: deployer.address, - amount: uint256.bnToUint256(giftAmount), + amount: giftAmount, max_fee: giftMaxFee, token: tokenContract.address, - claim_pubkey: claimSigner.publicKey, + claim_pubkey: claimPubKey, + }; + return { claim, claimPrivateKey: claimSigner.privateKey }; +} + +export function calculateClaimAddress(claim: Claim): string { + const constructorArgs: AccountConstructorArguments = { + sender: claim.sender, + amount: claim.amount, + max_fee: claim.max_fee, + token: claim.token, + claim_pubkey: claim.claim_pubkey, }; const claimAddress = hash.calculateContractAddressFromHash( 0, - claimAccountClassHash, - CallData.compile({ constructorArgs }), - factory.address, + claim.class_hash, + CallData.compile({ ...constructorArgs, amount: uint256.bnToUint256(claim.amount) }), + claim.factory, ); - - return { claimAddress, tokenContract, claimSigner }; + return claimAddress; } diff --git a/tests-integration/account.test.ts b/tests-integration/account.test.ts index bab1e5a..8bde5aa 100644 --- a/tests-integration/account.test.ts +++ b/tests-integration/account.test.ts @@ -1,13 +1,13 @@ import { expect } from "chai"; import { num } from "starknet"; import { - GIFT_AMOUNT, GIFT_MAX_FEE, + calculateClaimAddress, claimInternal, defaultDepositTestSetup, - deployer, expectRevertWithErrorMessage, manager, + randomReceiver, setupGiftProtocol, } from "../lib"; @@ -15,123 +15,122 @@ describe("Gifting", function () { for (const useTxV3 of [false, true]) { it(`Testing simple claim flow using txV3: ${useTxV3}`, async function () { const { factory } = await setupGiftProtocol(); - const { tokenContract, claimSigner, claimAddress } = await defaultDepositTestSetup(factory); - const receiver = `0x2${Math.floor(Math.random() * 1000)}`; - await claimInternal(factory, tokenContract, claimSigner.privateKey, claimSigner.publicKey, receiver, useTxV3); + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); + await claimInternal(claim, receiver, claimPrivateKey); - const finalBalance = await tokenContract.balance_of(claimAddress); - expect(finalBalance < GIFT_MAX_FEE).to.be.true; - await tokenContract.balance_of(receiver).should.eventually.equal(GIFT_AMOUNT); + const claimAddress = calculateClaimAddress(claim); + + const token = await manager.loadContract(claim.token); + const finalBalance = await token.balance_of(claimAddress); + expect(finalBalance < claim.max_fee).to.be.true; + await token.balance_of(receiver).should.eventually.equal(claim.amount); }); - it(`Test max fee too high`, async function () { + it(`Test max fee too high using txV3: ${useTxV3}`, async function () { const { factory } = await setupGiftProtocol(); - const { claim, receiver, claimAccount } = await defaultDepositTestSetup(factory, useTxV3); + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); if (useTxV3) { - const estimate = await factory.estimateFee.claim_internal(claim, receiver); const newResourceBounds = { - ...estimate.resourceBounds, l2_gas: { - ...estimate.resourceBounds.l2_gas, - max_amount: GIFT_MAX_FEE + 1n, - max_price_per_unit: num.toHexString(4), + max_amount: num.toHexString(GIFT_MAX_FEE * 1000n), + max_price_per_unit: num.toHexString(10), + }, + l1_gas: { + max_amount: num.toHexString(GIFT_MAX_FEE * 1000n), + max_price_per_unit: num.toHexString(10), }, + tip: 1, }; await expectRevertWithErrorMessage("gift-acc/max-fee-too-high-v3", () => - claimAccount.execute( - [ - { - contractAddress: factory.address, - calldata: [claim, receiver], - entrypoint: "claim_internal", - }, - ], - undefined, - { resourceBounds: newResourceBounds, tip: 1 }, - ), + claimInternal(claim, receiver, claimPrivateKey, { resourceBounds: newResourceBounds }), ); } else { await expectRevertWithErrorMessage("gift-acc/max-fee-too-high-v1", () => - factory.claim_internal(claim, receiver, { maxFee: GIFT_MAX_FEE + 1n }), + claimInternal(claim, receiver, claimPrivateKey, { + maxFee: GIFT_MAX_FEE + 1n, + }), ); } }); } - it(`Test only protocol can call claim contract`, async function () { - const { factory } = await setupGiftProtocol(); - const { claimAccount } = await defaultDepositTestSetup(factory); - const claimContract = await manager.loadContract(num.toHex(claimAccount.address)); + // it(`Test only protocol can call claim contract`, async function () { + // const { factory } = await setupGiftProtocol(); + // const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + // const receiver = randomReceiver(); - claimContract.connect(claimAccount); - await expectRevertWithErrorMessage("gift-acc/only-protocol", () => claimContract.__validate__([])); - }); + // await expectRevertWithErrorMessage("gift-acc/only-protocol", () => + // claimInternal(factory, tokenContract, claimClassHash, claimPrivateKey, receiver), + // ); + // }); - it(`Test claim contract cant call another contract`, async function () { - const { factory, claimAccountClassHash } = await setupGiftProtocol(); - const { claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); + // it(`Test claim contract cant call another contract`, async function () { + // const { factory } = await setupGiftProtocol(); + // const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + // const receiver = randomReceiver(); - const fakeFactory = await manager.deployContract("GiftFactory", { - unique: true, - constructorCalldata: [claimAccountClassHash, deployer.address], - }); - fakeFactory.connect(claimAccount); - await expectRevertWithErrorMessage("gift-acc/invalid-call-to", () => - fakeFactory.claim_internal(claim, receiver, { maxFee: 400000000000000n }), - ); - }); + // const claimFakeFactory = { ...claim, factory: "0x123" }; + // await expectRevertWithErrorMessage("gift-acc/invalid-call-to", () => + // claimInternal(claimFakeFactory, claimPrivateKey, receiver), + // ); + // }); - it(`Test claim contract can only call 'claim_internal'`, async function () { - const { factory } = await setupGiftProtocol(); - const { claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); + // it(`Test claim contract can only call 'claim_internal'`, async function () { + // const { factory } = await setupGiftProtocol(); + // const { tokenContract, claimAddress, claimClassHash, claimPrivateKey } = await defaultDepositTestSetup(factory); + // const receiver = randomReceiver(); - factory.connect(claimAccount); - await expectRevertWithErrorMessage("gift-acc/invalid-call-selector", () => - factory.get_dust(claim, receiver, { maxFee: 400000000000000n }), - ); - }); + // const claimAccount = new Account(manager, num.toHex(claimAddress), claimPrivateKey, undefined); - it(`Test claim contract cant preform a multicall`, async function () { - const { factory } = await setupGiftProtocol(); - const { claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); - - await expectRevertWithErrorMessage("gift-acc/invalid-call-len", () => - claimAccount.execute([ - { - contractAddress: factory.address, - calldata: [claim, receiver], - entrypoint: "claim_internal", - }, - { - contractAddress: factory.address, - calldata: [claim, receiver], - entrypoint: "claim_internal", - }, - ]), - ); - }); + // let claim = buildClaim( + // factory, + // claimAccountClassHash, + // GIFT_AMOUNT, + // GIFT_MAX_FEE, + // tokenContract, + // claimSigner.publicKey, + // ); + + // factory.connect(claimAccount); + // await expectRevertWithErrorMessage("gift-acc/invalid-call-selector", () => + // factory.get_dust(claim, receiver, { maxFee: 400000000000000n }), + // ); + // }); + + // it(`Test claim contract cant preform a multicall`, async function () { + // const { factory } = await setupGiftProtocol(); + // const { tokenContract, claimSigner } = await defaultDepositTestSetup(factory); + // const receiver = randomReceiver(); + + // await expectRevertWithErrorMessage("gift-acc/invalid-call-len", () => + // claimAccount.execute([ + // { + // contractAddress: factory.address, + // calldata: [claim, receiver], + // entrypoint: "claim_internal", + // }, + // { + // contractAddress: factory.address, + // calldata: [claim, receiver], + // entrypoint: "claim_internal", + // }, + // ]), + // ); + // }); it(`Test cannot call 'claim_internal' twice`, async function () { const { factory } = await setupGiftProtocol(); - const { claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); // double claim - await factory.claim_internal(claim, receiver); + await claimInternal(claim, receiver, claimPrivateKey); await expectRevertWithErrorMessage("gift-acc/invalid-claim-nonce", () => - claimAccount.execute( - [ - { - contractAddress: factory.address, - calldata: [claim, receiver], - entrypoint: "claim_internal", - }, - ], - undefined, - { skipValidate: false }, - ), + claimInternal(claim, receiver, claimPrivateKey, { skipValidate: false }), ); }); - // TODO Tests: // - claim_external // - check with wrong claim data diff --git a/tests-integration/claim_external.test.ts b/tests-integration/claim_external.test.ts index 74502b0..200b235 100644 --- a/tests-integration/claim_external.test.ts +++ b/tests-integration/claim_external.test.ts @@ -1,16 +1,13 @@ -import { defaultDepositTestSetup, deployer, getClaimExternalData, setupGiftProtocol } from "../lib"; +import { claimExternal, defaultDepositTestSetup, randomReceiver, setupGiftProtocol } from "../lib"; describe("claim_external", function () { for (const useTxV3 of [false, true]) { it(`Testing claim_external flow using txV3: ${useTxV3}`, async function () { const { factory } = await setupGiftProtocol(); - const { claim, receiver, claimAccount, claimSigner } = await defaultDepositTestSetup(factory); + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); - const claimExternalData = await getClaimExternalData({ receiver }); - const signature = await claimSigner.signMessage(claimExternalData, claimAccount.address); - - factory.connect(deployer); - await factory.claim_external(claim, receiver, signature); + await claimExternal(claim, receiver, claimPrivateKey); }); } }); diff --git a/tests-integration/factory.test.ts b/tests-integration/factory.test.ts index 7813926..ae3f9b4 100644 --- a/tests-integration/factory.test.ts +++ b/tests-integration/factory.test.ts @@ -1,147 +1,114 @@ import { expect } from "chai"; -import { Account, CallData, RPC, hash, num, uint256 } from "starknet"; +import { ec, encode, num } from "starknet"; import { - AccountConstructorArguments, GIFT_AMOUNT, GIFT_MAX_FEE, LegacyStarknetKeyPair, + calculateClaimAddress, + claimInternal, defaultDepositTestSetup, deployer, expectRevertWithErrorMessage, genericAccount, manager, + randomReceiver, setupGiftProtocol, } from "../lib"; describe("Factory", function () { it(`Test calculate claim address`, async function () { - const { factory, claimAccountClassHash } = await setupGiftProtocol(); - const { tokenContract, claimSigner } = await defaultDepositTestSetup(factory); + const { factory } = await setupGiftProtocol(); + const { claim } = await defaultDepositTestSetup(factory); + const claimAddress = await factory.get_claim_address( - claimAccountClassHash, + claim.class_hash, deployer.address, GIFT_AMOUNT, GIFT_MAX_FEE, - tokenContract.address, - claimSigner.publicKey, + claim.token, + claim.claim_pubkey, ); - const constructorArgs: AccountConstructorArguments = { - sender: deployer.address, - amount: uint256.bnToUint256(GIFT_AMOUNT), - max_fee: GIFT_MAX_FEE, - token: tokenContract.address, - claim_pubkey: claimSigner.publicKey, - }; - - const correctAddress = hash.calculateContractAddressFromHash( - 0, - claimAccountClassHash, - CallData.compile({ constructorArgs }), - factory.address, - ); + const correctAddress = calculateClaimAddress(claim); expect(claimAddress).to.be.equal(num.toBigInt(correctAddress)); }); for (const useTxV3 of [false, true]) { it(`get_dust: ${useTxV3}`, async function () { const { factory } = await setupGiftProtocol(); - const { tokenContract, claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); - const receiverDust = `0x2${Math.floor(Math.random() * 1000)}`; + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); + const receiverDust = randomReceiver(); - await factory.claim_internal(claim, receiver); + await claimInternal(claim, receiver, claimPrivateKey); + const claimAddress = calculateClaimAddress(claim); + const token = await manager.loadContract(claim.token); // Final check - const dustBalance = await tokenContract.balance_of(claimAccount.address); + + const dustBalance = await token.balance_of(claimAddress); expect(dustBalance < GIFT_MAX_FEE).to.be.true; - await tokenContract.balance_of(receiver).should.eventually.equal(GIFT_AMOUNT); + await token.balance_of(receiver).should.eventually.equal(GIFT_AMOUNT); // Test dust - await tokenContract.balance_of(receiverDust).should.eventually.equal(0n); + await token.balance_of(receiverDust).should.eventually.equal(0n); factory.connect(deployer); await factory.get_dust(claim, receiverDust); - await tokenContract.balance_of(claimAccount.address).should.eventually.equal(0n); - await tokenContract.balance_of(receiverDust).should.eventually.equal(dustBalance); + await token.balance_of(claimAddress).should.eventually.equal(0n); + await token.balance_of(receiverDust).should.eventually.equal(dustBalance); }); } it(`Test Cancel Claim`, async function () { const { factory } = await setupGiftProtocol(); - const { tokenContract, claim, receiver, claimAccount } = await defaultDepositTestSetup(factory); + const { claim, claimPrivateKey } = await defaultDepositTestSetup(factory); + const receiver = randomReceiver(); + const token = await manager.loadContract(claim.token); + const claimAddress = calculateClaimAddress(claim); - const balanceSenderBefore = await tokenContract.balance_of(deployer.address); + const balanceSenderBefore = await token.balance_of(deployer.address); factory.connect(deployer); const { transaction_hash } = await factory.cancel(claim); const txFee = BigInt((await manager.getTransactionReceipt(transaction_hash)).actual_fee.amount); // Check balance of the sender is correct - await tokenContract + await token .balance_of(deployer.address) - .should.eventually.equal(balanceSenderBefore + GIFT_AMOUNT + GIFT_MAX_FEE - txFee); + .should.eventually.equal(balanceSenderBefore + claim.amount + claim.max_fee - txFee); // Check balance claim address address == 0 - await tokenContract.balance_of(claimAccount.address).should.eventually.equal(0n); + await token.balance_of(claimAddress).should.eventually.equal(0n); - factory.connect(claimAccount); - await expectRevertWithErrorMessage("gift/already-claimed-or-cancel", () => factory.claim_internal(claim, receiver)); + await expectRevertWithErrorMessage("gift/already-claimed-or-cancel", () => + claimInternal(claim, receiver, claimPrivateKey), + ); }); - it(`Test pausable`, async function () { + it.only(`Test pausable`, async function () { // Deploy factory - const { factory, claimAccountClassHash } = await setupGiftProtocol(); - const signer = new LegacyStarknetKeyPair(); - const claimPubkey = signer.publicKey; - const GIFT_AMOUNT = 1000000000000000n; - const GIFT_MAX_FEE = 50000000000000n; - const receiver = `0x5${Math.floor(Math.random() * 1000)}`; - - // Make a gift + const { factory } = await setupGiftProtocol(); + const receiver = randomReceiver(); + const claimSigner = new LegacyStarknetKeyPair(`0x${encode.buf2hex(ec.starkCurve.utils.randomPrivateKey())}`); + + // approvals const tokenContract = await manager.tokens.feeTokenContract(false); tokenContract.connect(deployer); factory.connect(deployer); await tokenContract.approve(factory.address, GIFT_AMOUNT + GIFT_MAX_FEE); + // pause / unpause factory.connect(genericAccount); await expectRevertWithErrorMessage("Caller is not the owner", () => factory.pause()); factory.connect(deployer); await factory.pause(); await expectRevertWithErrorMessage("Pausable: paused", () => - factory.deposit(GIFT_AMOUNT, GIFT_MAX_FEE, tokenContract.address, claimPubkey), + factory.deposit(GIFT_AMOUNT, GIFT_MAX_FEE, tokenContract.address, claimSigner.publicKey), ); await factory.unpause(); - await factory.deposit(GIFT_AMOUNT, GIFT_MAX_FEE, tokenContract.address, claimPubkey); - - // Ensure there is a contract for the claim - const claimAddress = await factory.get_claim_address( - claimAccountClassHash, - deployer.address, - GIFT_AMOUNT, - GIFT_MAX_FEE, - tokenContract.address, - claimPubkey, - ); - - const claim = { - factory: factory.address, - class_hash: claimAccountClassHash, - sender: deployer.address, - amount: uint256.bnToUint256(GIFT_AMOUNT), - max_fee: GIFT_MAX_FEE, - token: tokenContract.address, - claim_pubkey: claimPubkey, - }; - - const claimContract = await manager.loadContract(num.toHex(claimAddress)); - const claimAccount = new Account(manager, claimContract.address, signer, undefined, RPC.ETransactionVersion.V2); - - // Check balance of the claim contract is correct - await tokenContract.balance_of(claimAddress).should.eventually.equal(GIFT_AMOUNT + GIFT_MAX_FEE); - // Check balance receiver address == 0 - await tokenContract.balance_of(receiver).should.eventually.equal(0n); - - factory.connect(claimAccount); - await factory.claim_internal(claim, receiver); + const { claim } = await defaultDepositTestSetup(factory, false, claimSigner.privateKey); + await claimInternal(claim, receiver, claimSigner.privateKey); // Final check + const claimAddress = calculateClaimAddress(claim); const dustBalance = await tokenContract.balance_of(claimAddress); expect(dustBalance < GIFT_MAX_FEE).to.be.true; await tokenContract.balance_of(receiver).should.eventually.equal(GIFT_AMOUNT);