@@ -464,9 +464,9 @@ describe("Superfluid Factoring", function () {
464464 // `payer ${payer.address} usdcx balance: ${await usdcx.balanceOf(payer.address)}`
465465 // );
466466
467- streamAmount = 2000 ;
468- streamDays = 10 ;
469- streamDuration = 10 * 24 * 60 * 60 ;
467+ streamAmount = 6000 ;
468+ streamDays = 30 ;
469+ streamDuration = streamDays * 24 * 60 * 60 ;
470470
471471 let flowrate = toDefaultToken ( streamAmount ) . div ( BN . from ( streamDuration ) ) ;
472472 await createFlow ( usdcx , payer , borrower , flowrate ) ;
@@ -475,7 +475,7 @@ describe("Superfluid Factoring", function () {
475475 await authorizeFlow ( usdcx , payer , nftContract ) ;
476476
477477 // console.log(`mint TradableStream...`);
478- collateralAmount = 500 ;
478+ collateralAmount = 1500 ;
479479 flowrate = toDefaultToken ( collateralAmount ) . div ( BN . from ( streamDuration ) ) . add ( BN . from ( 1 ) ) ;
480480 await nftContract
481481 . connect ( borrower )
@@ -4182,4 +4182,317 @@ describe("Superfluid Factoring", function () {
41824182 // printRecord(cr, crs);
41834183 // });
41844184 } ) ;
4185+
4186+ describe ( "Use front loading fee instead of interest apr" , function ( ) {
4187+ beforeEach ( async function ( ) {
4188+ const frontLoadingFeeBps = 100 ;
4189+ await feeManagerContract . connect ( poolOwner ) . setFees ( 0 , frontLoadingFeeBps , 0 , 0 , 0 ) ;
4190+ let result = await feeManagerContract . getFees ( ) ;
4191+ // console.log("fees: " + result);
4192+
4193+ await poolContract
4194+ . connect ( eaServiceAccount )
4195+ [ "approveCredit(address,uint256,uint256,uint256,uint256,address,uint256,uint256)" ] (
4196+ borrower . address ,
4197+ toUSDC ( streamAmount ) ,
4198+ streamDays ,
4199+ 1 ,
4200+ 0 ,
4201+ nftContract . address ,
4202+ ethers . utils . solidityKeccak256 (
4203+ [ "address" , "address" , "address" ] ,
4204+ [ usdcx . address , payer . address , borrower . address ]
4205+ ) ,
4206+ toUSDC ( streamAmount )
4207+ ) ;
4208+ await usdc . connect ( borrower ) . approve ( poolProcessorContract . address , toUSDC ( 10_000 ) ) ;
4209+
4210+ const beforeAmount = await usdc . balanceOf ( borrower . address ) ;
4211+
4212+ let flowrate = toDefaultToken ( collateralAmount )
4213+ . div ( BN . from ( streamDuration ) )
4214+ . add ( BN . from ( 1 ) ) ;
4215+ loanAmount = flowrate . mul ( BN . from ( streamDuration ) ) ;
4216+ const nonce = await nftContract . nonces ( borrower . address ) ;
4217+ const expiry = Math . ceil ( Date . now ( ) / 1000 ) + 300 ;
4218+ const signatureData = await borrower . _signTypedData (
4219+ {
4220+ name : "TradableStream" ,
4221+ version : nftVersion ,
4222+ chainId : HARDHAT_CHAIN_ID ,
4223+ verifyingContract : nftContract . address ,
4224+ } ,
4225+ {
4226+ MintToWithAuthorization : [
4227+ { name : "receiver" , type : "address" } ,
4228+ { name : "token" , type : "address" } ,
4229+ { name : "origin" , type : "address" } ,
4230+ { name : "owner" , type : "address" } ,
4231+ { name : "flowrate" , type : "int96" } ,
4232+ { name : "durationInSeconds" , type : "uint256" } ,
4233+ { name : "nonce" , type : "uint256" } ,
4234+ { name : "expiry" , type : "uint256" } ,
4235+ ] ,
4236+ } ,
4237+ {
4238+ receiver : borrower . address ,
4239+ token : usdcx . address ,
4240+ origin : payer . address ,
4241+ owner : poolProcessorContract . address ,
4242+ flowrate : flowrate ,
4243+ durationInSeconds : streamDuration ,
4244+ nonce : nonce ,
4245+ expiry : expiry ,
4246+ }
4247+ ) ;
4248+ const signature = ethers . utils . splitSignature ( signatureData ) ;
4249+ const calldata = ethers . utils . defaultAbiCoder . encode (
4250+ [
4251+ "address" ,
4252+ "address" ,
4253+ "address" ,
4254+ "int96" ,
4255+ "uint256" ,
4256+ "uint256" ,
4257+ "uint8" ,
4258+ "bytes32" ,
4259+ "bytes32" ,
4260+ ] ,
4261+ [
4262+ borrower . address ,
4263+ usdcx . address ,
4264+ payer . address ,
4265+ flowrate ,
4266+ streamDuration ,
4267+ expiry ,
4268+ signature . v ,
4269+ signature . r ,
4270+ signature . s ,
4271+ ]
4272+ ) ;
4273+ await poolProcessorContract . mintAndDrawdown (
4274+ borrower . address ,
4275+ loanAmount ,
4276+ nftContract . address ,
4277+ calldata
4278+ ) ;
4279+
4280+ const afterAmount = await usdc . balanceOf ( borrower . address ) ;
4281+ const receivedAmount = afterAmount . sub ( beforeAmount ) ;
4282+ const fee = loanAmount . mul ( BN . from ( frontLoadingFeeBps ) ) . div ( BN . from ( 10000 ) ) ;
4283+
4284+ expect ( receivedAmount ) . to . equal ( loanAmount . sub ( fee ) ) ;
4285+ } ) ;
4286+
4287+ it ( "Should pay off correctly" , async function ( ) {
4288+ let cr = await poolContract . creditRecordMapping ( borrower . address ) ;
4289+ let crs = await poolContract . creditRecordStaticMapping ( borrower . address ) ;
4290+ // printRecord(cr, crs);
4291+
4292+ const expiration = 10000 ;
4293+ const nts = cr . dueDate . toNumber ( ) + expiration ;
4294+ let block = await ethers . provider . getBlock ( ) ;
4295+ const beforeBorrowerFlowrate = await cfa . getNetFlow ( usdcx . address , borrower . address ) ;
4296+ const beforeReceivedAmount = BN . from ( nts )
4297+ . sub ( block . timestamp )
4298+ . mul ( beforeBorrowerFlowrate ) ;
4299+ await setNextBlockTimestamp ( nts ) ;
4300+ const streamId = 1 ;
4301+ let res = await nftContract . getTradableStreamData ( streamId ) ;
4302+ const flowrate = res [ 6 ] ;
4303+ const si = await poolProcessorContract . streamInfoMapping ( streamId ) ;
4304+ const beforeBorrowAmount = await usdc . balanceOf ( borrower . address ) ;
4305+ const beforePoolAmount = await usdc . balanceOf ( poolContract . address ) ;
4306+ const beforeBorrowXAmount = await usdcx . balanceOf ( borrower . address ) ;
4307+ const beforeProcessorFlowrate = await cfa . getNetFlow (
4308+ usdcx . address ,
4309+ poolProcessorContract . address
4310+ ) ;
4311+ await expect ( poolProcessorContract . settlement ( nftContract . address , streamId ) )
4312+ . to . emit ( poolProcessorContract , "SettlementMade" )
4313+ . withArgs (
4314+ poolContract . address ,
4315+ borrower . address ,
4316+ si . flowKey ,
4317+ nftContract . address ,
4318+ streamId
4319+ ) ;
4320+ const afterBorrowAmount = await usdc . balanceOf ( borrower . address ) ;
4321+ const afterPoolAmount = await usdc . balanceOf ( poolContract . address ) ;
4322+ const afterBorrowXAmount = await usdcx . balanceOf ( borrower . address ) ;
4323+ const afterProcessorFlowrate = await cfa . getNetFlow (
4324+ usdcx . address ,
4325+ poolProcessorContract . address
4326+ ) ;
4327+ const afterBorrowerFlowrate = await cfa . getNetFlow ( usdcx . address , borrower . address ) ;
4328+ // console.log(
4329+ // `afterBorrowAmount: ${afterBorrowAmount}, beforeBorrowAmount: ${beforeBorrowAmount}`
4330+ // );
4331+ expect ( afterBorrowAmount . sub ( beforeBorrowAmount ) ) . to . equal ( 0 ) ;
4332+ expect ( afterPoolAmount . sub ( beforePoolAmount ) ) . to . equal ( loanAmount ) ;
4333+ expect ( beforeProcessorFlowrate . sub ( afterProcessorFlowrate ) ) . to . equal (
4334+ afterBorrowerFlowrate . sub ( beforeBorrowerFlowrate )
4335+ ) ;
4336+ expect ( afterBorrowerFlowrate . sub ( beforeBorrowerFlowrate ) ) . to . equal ( flowrate ) ;
4337+ await expect ( nftContract . ownerOf ( streamId ) ) . to . be . revertedWith (
4338+ "ERC721: invalid token ID"
4339+ ) ;
4340+ expect ( afterBorrowXAmount . sub ( beforeBorrowXAmount ) . sub ( beforeReceivedAmount ) ) . to . equal (
4341+ si . flowrate . mul ( BN . from ( expiration ) )
4342+ ) ;
4343+ cr = await poolContract . creditRecordMapping ( borrower . address ) ;
4344+ crs = await poolContract . creditRecordStaticMapping ( borrower . address ) ;
4345+ // printRecord(cr, crs);
4346+ checkRecord (
4347+ cr ,
4348+ crs ,
4349+ toUSDC ( streamAmount ) ,
4350+ 0 ,
4351+ "SKIP" ,
4352+ "SKIP" ,
4353+ 0 ,
4354+ 0 ,
4355+ 0 ,
4356+ 0 ,
4357+ 0 ,
4358+ streamDays ,
4359+ 0 ,
4360+ 0
4361+ ) ;
4362+ checkResults ( await poolContract . receivableInfoMapping ( borrower . address ) , [
4363+ ethers . constants . AddressZero ,
4364+ 0 ,
4365+ 0 ,
4366+ ] ) ;
4367+ checkResults ( await poolProcessorContract . streamInfoMapping ( streamId ) , [
4368+ ethers . constants . AddressZero ,
4369+ 0 ,
4370+ 0 ,
4371+ 0 ,
4372+ 0 ,
4373+ ethers . constants . HashZero ,
4374+ ] ) ;
4375+
4376+ const flowId = ethers . utils . keccak256 (
4377+ ethers . utils . defaultAbiCoder . encode (
4378+ [ "address" , "address" ] ,
4379+ [ payer . address , poolProcessorContract . address ]
4380+ )
4381+ ) ;
4382+ const flowKey = ethers . utils . solidityKeccak256 (
4383+ [ "address" , "bytes32" ] ,
4384+ [ usdcx . address , flowId ]
4385+ ) ;
4386+ expect ( await poolProcessorContract . flowEndMapping ( flowKey ) ) . to . equal ( 0 ) ;
4387+ } ) ;
4388+
4389+ it ( "Should pay off correctly if the flow was terminated" , async function ( ) {
4390+ await sfRegisterContract . register ( poolProcessorContract . address ) ;
4391+
4392+ let balance = await usdc . balanceOf ( borrower . address ) ;
4393+ let remainingBal = toUSDC ( 10 ) ;
4394+ if ( balance . gt ( remainingBal ) ) {
4395+ await usdc
4396+ . connect ( borrower )
4397+ . transfer ( defaultDeployer . address , balance . sub ( remainingBal ) ) ;
4398+ } else {
4399+ remainingBal = balance ;
4400+ }
4401+
4402+ let cr = await poolContract . creditRecordMapping ( borrower . address ) ;
4403+ const remainingTime = 3600 * 24 * 7 ;
4404+ let nts = cr . dueDate . toNumber ( ) - remainingTime ;
4405+ await setNextBlockTimestamp ( nts ) ;
4406+
4407+ await deleteFlow ( usdcx , payer , poolProcessorContract ) ;
4408+
4409+ const streamId = 1 ;
4410+ const flowId = ethers . utils . keccak256 (
4411+ ethers . utils . defaultAbiCoder . encode (
4412+ [ "address" , "address" ] ,
4413+ [ payer . address , poolProcessorContract . address ]
4414+ )
4415+ ) ;
4416+ const flowKey = ethers . utils . solidityKeccak256 (
4417+ [ "address" , "bytes32" ] ,
4418+ [ usdcx . address , flowId ]
4419+ ) ;
4420+ let beforeSI = await poolProcessorContract . streamInfoMapping ( streamId ) ;
4421+ let beforeBorrowerBal = await usdc . balanceOf ( borrower . address ) ;
4422+ let beforePoolBal = await usdc . balanceOf ( poolContract . address ) ;
4423+ await poolProcessorContract . onTerminatedFlow ( flowKey , streamId ) ;
4424+ let afterBorrowerBal = await usdc . balanceOf ( borrower . address ) ;
4425+ let afterPoolBal = await usdc . balanceOf ( poolContract . address ) ;
4426+ let afterSI = await poolProcessorContract . streamInfoMapping ( streamId ) ;
4427+
4428+ expect ( beforeBorrowerBal . sub ( afterBorrowerBal ) ) . to . equal ( remainingBal ) ;
4429+ expect ( afterPoolBal . sub ( beforePoolBal ) ) . to . equal ( remainingBal ) ;
4430+ expect ( afterSI . lastStartTime ) . to . equal ( nts ) ;
4431+ expect ( afterSI . flowrate ) . to . equal ( 0 ) ;
4432+ expect ( afterSI . endTime ) . to . equal ( beforeSI . endTime ) ;
4433+ expect ( afterSI . receivedFlowAmount ) . to . equal (
4434+ BN . from ( nts ) . sub ( beforeSI . lastStartTime ) . mul ( beforeSI . flowrate )
4435+ ) ;
4436+
4437+ cr = await poolContract . creditRecordMapping ( borrower . address ) ;
4438+ crs = await poolContract . creditRecordStaticMapping ( borrower . address ) ;
4439+ let block = await ethers . provider . getBlock ( ) ;
4440+ let correction = calcCorrection ( cr , crs , block . timestamp , remainingBal ) ;
4441+ // console.log("correction: " + correction);
4442+ checkRecord (
4443+ cr ,
4444+ crs ,
4445+ toUSDC ( streamAmount ) ,
4446+ 0 ,
4447+ "SKIP" ,
4448+ correction ,
4449+ loanAmount . sub ( remainingBal ) ,
4450+ 0 ,
4451+ 0 ,
4452+ 0 ,
4453+ 0 ,
4454+ streamDays ,
4455+ 3 ,
4456+ 0
4457+ ) ;
4458+
4459+ await mint ( borrower . address , toUSDC ( streamAmount ) ) ;
4460+
4461+ const expiration = 1000 ;
4462+ nts = cr . dueDate . toNumber ( ) + expiration ;
4463+ await setNextBlockTimestamp ( nts ) ;
4464+
4465+ beforeBorrowerBal = await usdc . balanceOf ( borrower . address ) ;
4466+ beforePoolBal = await usdc . balanceOf ( poolContract . address ) ;
4467+ await poolProcessorContract . settlement ( nftContract . address , streamId ) ;
4468+ afterBorrowerBal = await usdc . balanceOf ( borrower . address ) ;
4469+ afterPoolBal = await usdc . balanceOf ( poolContract . address ) ;
4470+
4471+ expect ( afterPoolBal . sub ( beforePoolBal ) ) . to . equal (
4472+ loanAmount . sub ( remainingBal ) . add ( correction )
4473+ ) ;
4474+ expect ( beforeBorrowerBal . sub ( afterBorrowerBal ) ) . to . equal (
4475+ loanAmount . sub ( remainingBal ) . sub ( afterSI . receivedFlowAmount ) . add ( correction )
4476+ ) ;
4477+
4478+ cr = await poolContract . creditRecordMapping ( borrower . address ) ;
4479+ crs = await poolContract . creditRecordStaticMapping ( borrower . address ) ;
4480+ checkRecord (
4481+ cr ,
4482+ crs ,
4483+ toUSDC ( streamAmount ) ,
4484+ 0 ,
4485+ "SKIP" ,
4486+ 0 ,
4487+ 0 ,
4488+ 0 ,
4489+ 0 ,
4490+ 0 ,
4491+ 0 ,
4492+ streamDays ,
4493+ 0 ,
4494+ 0
4495+ ) ;
4496+ } ) ;
4497+ } ) ;
41854498} ) ;
0 commit comments