UNPKG

@nitin-spheron/desub-js

Version:

Typescript library for working with the decentralised subscription contracts in Spheron

795 lines (688 loc) 28.1 kB
import 'mocha' import { stub } from 'sinon' import { assert, expect } from 'chai' import Vendor from '../vendors/ethers' import Payment from '../payment' import { JsonRpcProvider, TransactionReceipt } from '@ethersproject/providers' import { Wallet } from '@ethersproject/wallet' import { cloneWithWriteAccess } from '../helpers' import { Contract, TxResponse } from '../interfaces' import { INVALID_API_KEY } from '../errors' import * as dotenv from 'dotenv' import { BigNumber } from 'ethers' import cloneDeep from 'lodash.clonedeep' describe('Payments methods', () => { let payment: Payment let vendor: Vendor const invalidKey = 'api-key' let correctKey: any let biconomyKey: any beforeEach(async () => { dotenv.config() correctKey = process.env.COINMARKETCAP_KEY biconomyKey = process.env.BICONOMY_KEY const url = process.env.RPC_ENDPOINT const httpProvider = new JsonRpcProvider(url) const mnemonic: any = process.env.MNEMONIC_TEST const signer = Wallet.fromMnemonic(mnemonic).connect(httpProvider) vendor = new Vendor(httpProvider, signer, biconomyKey) payment = new Payment(vendor, invalidKey) await payment.at('0x113bcF2d1DeB08D295291dA8Bce33ACAD9c9A726', '0xE044842Ce0A54dF5Dc11dbB962B462B28331728e') }) it('it should pay with fee', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'chargeWithProvider') fake.resolves({ hash: '0xhash' }) const address = '0x123' const buildTime = '12' const uploadFee = '123' const deploymentQuote = '123' const deploymentCharge = '123' const provider = 'abc' const result: TxResponse = await payment.paymentWithFee( address, buildTime, uploadFee, deploymentQuote, deploymentCharge, provider, ) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.deepEqual(args[0], address) assert.deepEqual(args[1], vendor.convertToBN(buildTime)) assert.deepEqual(args[2], vendor.convertToWei(uploadFee, 18)) assert.deepEqual(args[3], vendor.convertToWei(deploymentQuote, 18)) assert.deepEqual(args[4], vendor.convertToWei(deploymentCharge, 18)) assert.deepEqual(args[5], provider) }).timeout(500000) it('it should pay without fee', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'charge') fake.resolves({ hash: '0xhash' }) const address = '0x123' const buildTime = '12' const result: TxResponse = await payment.paymentWithoutFee(address, buildTime) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.deepEqual(args[0], address) assert.deepEqual(args[1], vendor.convertToBN(buildTime)) }) it('it should pass correct escrow address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateEscrow') fake.resolves({ hash: '0xhash' }) const addr = '0x1abc' const result: TxResponse = await payment.updateEscrow(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass correct token address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateToken') fake.resolves({ hash: '0xhash' }) const addr = '0x1abc' const result: TxResponse = await payment.updateToken(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass underlying token address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateUnderlyingToken') fake.resolves({ hash: '0xhash' }) const addr = '0x1abc' const result: TxResponse = await payment.updateUnderlyingToken(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass correct feeder address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateFeederAddress') fake.resolves({ hash: '0xhash' }) const addr = '0x1abc' const result: TxResponse = await payment.updateFeederAddress(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass correct staked token address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateStakedToken') fake.resolves({ hash: '0xhash' }) const addr = '0x1abc' const result: TxResponse = await payment.updateStakedToken(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass correct slabs array', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'updateDiscountSlabs') fake.resolves({ hash: '0xhash' }) const discountSlabs = ['1200', '1300', '1400'] const percents = ['12', '13', '14'] const result: TxResponse = await payment.updateDiscountSlabs(discountSlabs, percents) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.deepEqual(args[0], vendor.convertStringArrayToBigNumberArray(discountSlabs)) assert.deepEqual(args[1], vendor.convertStringArrayToBigNumberArray(percents)) }) it('it should pass correct price', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'changeBuildTimeRate') fake.resolves({ hash: '0xhash' }) const price = '12' const result: TxResponse = await payment.changeBuildTimeRate(price) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.deepEqual(args[0], vendor.convertToWei(price, 18)) }) it('it should pass correct staking address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'enableDiscounts') fake.resolves({ hash: '0xhash' }) const stakingAddr = '0xabc' const result: TxResponse = await payment.enableDiscounts(stakingAddr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], stakingAddr) }) it('it should return hash', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'disableDiscounts') fake.resolves({ hash: '0xhash' }) const result: TxResponse = await payment.disableDiscounts() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') }) it('it should pass correct address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'setGovernanceAddress') fake.resolves({ hash: '0xhash' }) const addr = '0xabc' const result: TxResponse = await payment.setGovernanceAddress(addr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], addr) }) it('it should pass correct array of address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'setManagers') fake.resolves({ hash: '0xhash' }) const arr = ['0x1bw', '0xasb'] const result: TxResponse = await payment.setManagers(arr) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.deepEqual(args[0], arr) }) it('it should pass correct array of address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract const paymentsContract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'approve') fake.resolves({ hash: '0xhash' }) const amount = '12' const result: TxResponse = await payment.setNewApprovals(amount) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0xhash') const { args } = fake.getCall(0) assert.equal(args[0], paymentsContract.address) assert.deepEqual(args[1], vendor.convertToWei(amount, 18)) }) it('it should get correct approval amount', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract const paymentsContract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'allowance') fake.resolves(vendor.convertToBN('1000000000000000000000000')) const arg1 = '0x123' const result: any = await payment.getApprovalAmount(arg1) assert(fake.calledOnce) assert.isNotNull(result) assert.deepEqual(result, vendor.convertWeiToEth(vendor.convertToBN('1000000000000000000000000'), 18)) const { args } = fake.getCall(0) assert.equal(args[0], arg1) assert.equal(args[1], paymentsContract.address) }) it('it should get correct balance of user', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'balanceOf') fake.resolves(vendor.convertToBN('1000000000000000000000000')) const arg1 = '0x123' const result: any = await payment.getUserBalance(arg1) assert(fake.calledOnce) assert.isNotNull(result) assert.deepEqual(result, vendor.convertWeiToEth(vendor.convertToBN('1000000000000000000000000'), 18)) const { args } = fake.getCall(0) assert.equal(args[0], arg1) }) it('it should give correct address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'getManagers') fake.resolves('0x123') const result = await payment.getManagers() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, ['0x123']) }) it('it should give correct governance address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'governanceAddress') fake.resolves('0x123') const result = await payment.getGovernanceAddress() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, '0x123') }) it('it should give correct token address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'underlying') fake.resolves('0x123') const result = await payment.getToken() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, '0x123') }) it('it should give correct escrow address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'escrow') fake.resolves('0x123') const result = await payment.getEscrow() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, '0x123') }) it('it should give correct discount status', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'discountsEnabled') fake.resolves(true) const result = await payment.checkIfDiscountsEnabled() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, true) }) it('it should give correct staking manager address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'stakingManager') fake.resolves('0x123') const result = await payment.getStakingManagerAddress() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, '0x123') }) it('it should give correct staked token address', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'stakedToken') fake.resolves('0x123') const result = await payment.getStakedTokenAddress() assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result, '0x123') }) it('it should give correct staking manager address', async () => { // allow stubbing contract properties payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const user = '0x123' const fake = stub(contract.functions, 'getNonce') fake.resolves([BigNumber.from(0)]) const result = await payment.getNonceForGaslessERC20(user) assert(fake.calledOnce) assert.isNotNull(result) const { args } = fake.getCall(0) assert.equal(args[0], user) assert.equal(result, [BigNumber.from(0)][0].toNumber()) }) it('it should give correct discount slabs', async () => { // allow stubbing contract properties payment.paymentsContract = cloneWithWriteAccess(payment.paymentsContract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.paymentsContract ? payment.paymentsContract : invalidContract assert.isNotNull(contract) assert.notDeepEqual(contract, invalidContract) const fake = stub(contract.functions, 'discountSlabs') fake.resolves([ { amount: vendor.convertToBN('10'), percent: vendor.convertToBN('10') }, { amount: vendor.convertToBN('11'), percent: vendor.convertToBN('11') }, ]) const result = await payment.getDiscountSlabs() assert(fake.calledOnce) assert.isNotNull(result) assert.deepEqual( result, vendor.parseDiscountSlabs([ { amount: vendor.convertToBN('10'), percent: vendor.convertToBN('10') }, { amount: vendor.convertToBN('11'), percent: vendor.convertToBN('11') }, ]), ) }) it('should throw with invalid api key', async () => { try { await payment.getArweaveConvertedUsd('2') } catch (err: any) { expect(err.toString()).deep.equal(new Error(INVALID_API_KEY).toString()) } }) it('It should return arweave usd quote', async () => { payment = new Payment(vendor, correctKey) const result = await payment.getArweaveConvertedUsd('2') assert.notEqual(result, 0) }) it('should throw with invalid api key', async () => { try { await payment.getArweaveQuote() } catch (err: any) { expect(err.toString()).deep.equal(new Error(INVALID_API_KEY).toString()) } }) it('It should return arweave usd quote', async () => { payment = new Payment(vendor, correctKey) const result = await payment.getArweaveQuote() assert.notEqual(result, 0) }) it('should throw with invalid api key', async () => { try { await payment.getAkashConvertedUsd('2') } catch (err: any) { expect(err.toString()).deep.equal(new Error(INVALID_API_KEY).toString()) } }) it('It should return arweave usd quote', async () => { payment = new Payment(vendor, correctKey) const result = await payment.getAkashConvertedUsd('2') assert.notEqual(result, 0) }) it('should throw with invalid api key', async () => { try { await payment.getAkashQuote() } catch (err: any) { expect(err.toString()).deep.equal(new Error(INVALID_API_KEY).toString()) } }).timeout(5000) it('It should return arweave usd quote', async () => { payment = new Payment(vendor, correctKey) const result = await payment.getAkashQuote() assert.notEqual(result, 0) }) it('it should pass correct values for gassless transaction', async () => { payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const contract: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract const fakeNonce = stub(contract.functions, 'getNonce') fakeNonce.resolves([BigNumber.from(0)]) const fake = stub(payment, 'sendRawBiconomyERC20Transaction') fake.resolves({ hash: '0x123', confirmations: 0, from: '0x123', to: '0x123', gasLimit: BigNumber.from(0), gasPrice: BigNumber.from(0), nonce: 0, data: 'Ad', value: BigNumber.from(0), chainId: 0, // eslint-disable-next-line @typescript-eslint/no-empty-function wait: () => new Promise<TransactionReceipt>(() => {}), }) const chainID = 80001 const wei = vendor.convertToWei('10', 18) const abiEncodedApprove = vendor.abiEncodeErc20Functions('approve', [payment.paymentsContract?.address, wei]) const userAddress = await vendor.signer.getAddress() const nonce = await payment.getNonceForGaslessERC20(userAddress) const signedMessage = await vendor.signedMessageForTx( userAddress, nonce, abiEncodedApprove, payment.erc20Contract?.address || '0x', chainID, ) const rsv = vendor.getSignatureParameters(signedMessage) const result: TxResponse = await payment.gasslessApproval('10', chainID) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0x123') const { args } = fake.getCall(0) assert.deepEqual(args[0], userAddress) assert.deepEqual(args[1], abiEncodedApprove) assert.deepEqual(args[2], rsv) }) it('it should pass correct values for meta transaction', async () => { payment.erc20Contract = cloneWithWriteAccess(payment.erc20Contract) payment.biconomyERC20Contract = cloneDeep(payment.biconomyERC20Contract) const invalidContract = { address: '0xinvalid', functions: {}, } const erc20: Contract = payment.erc20Contract ? payment.erc20Contract : invalidContract const contract: Contract = payment.biconomyERC20Contract ? payment.biconomyERC20Contract : invalidContract const fakeNonce = stub(erc20.functions, 'getNonce') fakeNonce.resolves([BigNumber.from(0)]) const fake = stub(contract.functions, 'executeMetaTransaction') fake.resolves({ hash: '0x123', confirmations: 0, from: '0x123', to: '0x123', gasLimit: BigNumber.from(0), gasPrice: BigNumber.from(0), nonce: 0, data: 'Ad', value: BigNumber.from(0), chainId: 0, wait: () => new Promise<TransactionReceipt>(() => { return undefined }), }) const chainID = 80001 const wei = vendor.convertToWei('10', 18) const abiEncodedApprove = vendor.abiEncodeErc20Functions('approve', [payment.paymentsContract?.address, wei]) const userAddress = await vendor.signer.getAddress() const nonce = await payment.getNonceForGaslessERC20(userAddress) const signedMessage = await vendor.signedMessageForTx( userAddress, nonce, abiEncodedApprove, payment.biconomyERC20Contract?.address || '0x', chainID, ) const rsv = vendor.getSignatureParameters(signedMessage) const result: TxResponse = await payment.gasslessApproval('10', chainID) assert(fake.calledOnce) assert.isNotNull(result) assert.equal(result.hash, '0x123') const { args } = fake.getCall(0) assert.deepEqual(args[0], userAddress) assert.deepEqual(args[1], abiEncodedApprove) assert.deepEqual(args[2], rsv.r) assert.deepEqual(args[3], rsv.s) assert.deepEqual(args[4], rsv.v) }).timeout(50000) })