From 97081d15c6ceb0691f570dbe619ebff850b28937 Mon Sep 17 00:00:00 2001 From: didi Date: Mon, 18 Nov 2024 16:26:16 +0100 Subject: [PATCH 01/11] delete broken examples --- README.md | 9 +- projects/borrow-against-salary/.env-template | 4 - projects/borrow-against-salary/README.md | 36 - .../cache/solidity-files-cache.json | 990 ------ .../contracts/EmploymentLoan.sol | 379 --- .../contracts/LoanFactory.sol | 54 - .../borrow-against-salary/hardhat.config.js | 38 - projects/borrow-against-salary/package.json | 29 - .../scripts/createEmployerFlow.js | 45 - .../scripts/deleteEmployerFlow.js | 45 - .../scripts/deployFactory.js | 43 - .../scripts/deployLoan.js | 67 - .../scripts/getLoanById.js | 41 - .../scripts/initialFunding.js | 51 - .../borrow-against-salary/scripts/lend.js | 49 - .../scripts/lenderApproval.js | 58 - .../scripts/updateEmployerFlow.js | 46 - .../test/EmploymentLoan.test.js | 788 ----- projects/flow-splitter/.gitignore | 11 - projects/flow-splitter/README.md | 11 - .../flow-splitter/contracts/FlowSplitter.sol | 220 -- projects/flow-splitter/hardhat.config.js | 6 - projects/flow-splitter/package.json | 11 - projects/flow-splitter/resources/diagram.png | Bin 64924 -> 0 bytes projects/flow-splitter/scripts/deploy.js | 40 - .../flow-splitter/test/FlowSplitter.test.js | 330 -- projects/growing-nft/.env.template | 3 - projects/growing-nft/.gitignore | 11 - projects/growing-nft/README.md | 68 - .../growing-nft/arguments-flower-goerli.js | 17 - .../growing-nft/arguments-testtoken-goerli.js | 18 - projects/growing-nft/contracts/Flower.sol | 257 -- projects/growing-nft/hardhat.config.js | 27 - projects/growing-nft/package.json | 14 - .../resources/flower-metadatas/plant1.json | 7 - .../resources/flower-metadatas/plant2.json | 7 - .../resources/flower-metadatas/plant3.json | 7 - .../resources/img/flower-growth.png | Bin 55802 -> 0 bytes .../resources/img/money-streaming-basic.gif | Bin 24249382 -> 0 bytes .../resources/img/plant-stages/plant1.png | Bin 8937 -> 0 bytes .../resources/img/plant-stages/plant2.png | Bin 7398 -> 0 bytes .../resources/img/plant-stages/plant3.png | Bin 8887 -> 0 bytes projects/growing-nft/resources/viz.pptx | Bin 120316 -> 0 bytes projects/growing-nft/scripts/deployFlower.js | 36 - .../growing-nft/scripts/mintSuperWater.js | 36 - projects/growing-nft/scripts/streamWater.js | 61 - projects/growing-nft/scripts/transferAway.js | 44 - .../growing-nft/scripts/utils/deployWater.js | 36 - .../scripts/utils/deployedContracts.js | 27 - .../scripts/utils/generateMetadata.js | 36 - projects/growing-nft/test/Flower.test.js | 387 --- .../instant-distribution-intro/.env.template | 7 - .../instant-distribution-intro/.eslintrc.js | 22 - .../instant-distribution-intro/.gitignore | 9 - .../.prettierignore | 5 - .../instant-distribution-intro/.solhint.json | 7 - .../instant-distribution-intro/.solhintignore | 1 - projects/instant-distribution-intro/README.md | 580 ---- .../arguments-tokenspreader.js | 6 - .../contracts/TokenSpreader.sol | 83 - .../hardhat.config.js | 40 - .../instant-distribution-intro/package.json | 24 - .../resources/high_level_viz.png | Bin 32105 -> 0 bytes .../scripts/cleanStatus.js | 82 - .../scripts/deleteShares.js | 87 - .../scripts/deploy.js | 46 - .../scripts/distribute.js | 93 - .../scripts/gainShare.js | 103 - .../scripts/loseShare.js | 87 - .../scripts/transferInSpreaderTokens.js | 100 - .../scripts/viewStatus.js | 101 - .../test/TokenSpreader.test.js | 517 --- .../money-streaming-intro-foundry/README.md | 19 - .../cache/solidity-files-cache.json | 1 - .../foundry.toml | 13 - .../lib/forge-std/.github/workflows/tests.yml | 27 - .../lib/forge-std/.gitignore | 4 - .../lib/forge-std/.gitmodules | 3 - .../lib/forge-std/LICENSE-APACHE | 203 -- .../lib/forge-std/LICENSE-MIT | 25 - .../lib/forge-std/README.md | 246 -- .../lib/forge-std/foundry.toml | 2 - .../lib/forge-std/lib/ds-test/.gitignore | 3 - .../lib/forge-std/lib/ds-test/LICENSE | 674 ---- .../lib/forge-std/lib/ds-test/Makefile | 14 - .../lib/forge-std/lib/ds-test/default.nix | 4 - .../lib/forge-std/lib/ds-test/demo/demo.sol | 222 -- .../lib/forge-std/lib/ds-test/src/test.sol | 469 --- .../lib/forge-std/package.json | 16 - .../lib/forge-std/src/Script.sol | 44 - .../lib/forge-std/src/StdJson.sol | 118 - .../lib/forge-std/src/Test.sol | 1138 ------- .../lib/forge-std/src/Vm.sol | 262 -- .../lib/forge-std/src/console.sol | 1533 --------- .../lib/forge-std/src/console2.sol | 1538 --------- .../lib/forge-std/src/test/Script.t.sol | 20 - .../forge-std/src/test/StdAssertions.t.sol | 602 ---- .../lib/forge-std/src/test/StdCheats.t.sol | 282 -- .../lib/forge-std/src/test/StdError.t.sol | 124 - .../lib/forge-std/src/test/StdMath.t.sol | 200 -- .../lib/forge-std/src/test/StdStorage.t.sol | 321 -- .../src/test/fixtures/broadcast.log.json | 187 -- .../src/MoneyRouter.sol | 138 - .../test/MoneyRouter.t.sol | 110 - .../.env.example | 3 - .../money-streaming-intro-hardhat/README.md | 17 - .../cache/solidity-files-cache.json | 913 ------ .../contracts/MoneyRouter.sol | 143 - .../hardhat.config.js | 37 - .../package.json | 26 - .../scripts/aclApproval.js | 63 - .../scripts/createFlowFromContract.js | 64 - .../scripts/createFlowIntoContract.js | 62 - .../scripts/deleteFlowFromContract.js | 64 - .../scripts/deleteFlowIntoContract.js | 62 - .../scripts/deploy.js | 45 - .../scripts/sendLumpSumToContract.js | 61 - .../scripts/tokenApproval.js | 63 - .../scripts/updateFlowFromContract.js | 64 - .../scripts/updateFlowIntoContract.js | 62 - .../scripts/withdrawFunds.js | 61 - .../test/MoneyRouter.test.js | 220 -- .../.env.template | 3 - .../superfluid-automated-vesting/README.md | 93 - .../build-and-test.sh | 18 - .../cache/solidity-files-cache.json | 1250 -------- .../hardhat.config.ts | 81 - .../superfluid-automated-vesting/package.json | 64 - .../scripts/Helpers.ts | 37 - .../scripts/createVestingSchedule.ts | 49 - .../scripts/createVestingTask.ts | 30 - .../scripts/deployVestingAutomation.ts | 48 - .../scripts/deployVestingScheduler.ts | 45 - .../src/VestingAutomation.sol | 225 -- .../src/VestingScheduler.sol | 254 -- .../src/gelato/AutomateReady.sol | 65 - .../src/gelato/AutomateTaskCreator.sol | 86 - .../src/gelato/Types.sol | 49 - .../src/interface/IVestingScheduler.sol | 227 -- .../test/VestingAutomation.test.ts | 1 - .../tsconfig.json | 19 - .../@openzeppelin/contracts/index.ts | 7 - .../contracts/token/ERC20/IERC20.ts | 342 -- .../draft-IERC20Permit.sol/IERC20Permit.ts | 193 -- .../draft-IERC20Permit.sol/index.ts | 4 - .../contracts/token/ERC20/extensions/index.ts | 5 - .../contracts/token/ERC20/index.ts | 6 - .../contracts/token/ERC721/IERC721.ts | 559 ---- .../ERC721/extensions/IERC721Metadata.ts | 619 ---- .../token/ERC721/extensions/index.ts | 4 - .../contracts/token/ERC721/index.ts | 6 - .../contracts/token/ERC777/IERC777.ts | 650 ---- .../contracts/token/ERC777/index.ts | 4 - .../@openzeppelin/contracts/token/index.ts | 9 - .../@openzeppelin/contracts/utils/index.ts | 5 - .../contracts/utils/introspection/IERC165.ts | 103 - .../contracts/utils/introspection/index.ts | 4 - .../typechain-types/@openzeppelin/index.ts | 5 - .../contracts/apps/SuperAppBase.ts | 457 --- .../contracts/apps/index.ts | 4 - .../ethereum-contracts/contracts/index.ts | 7 - .../agreements/IConstantFlowAgreementV1.ts | 1390 -------- .../contracts/interfaces/agreements/index.ts | 4 - .../contracts/interfaces/index.ts | 9 - .../superfluid/IConstantInflowNFT.ts | 876 ----- .../superfluid/IConstantOutflowNFT.ts | 945 ------ .../interfaces/superfluid/IFlowNFTBase.ts | 807 ----- .../interfaces/superfluid/ISuperAgreement.ts | 157 - .../interfaces/superfluid/ISuperApp.ts | 457 --- .../interfaces/superfluid/ISuperToken.ts | 2806 ----------------- .../superfluid/ISuperTokenFactory.ts | 465 --- .../interfaces/superfluid/ISuperfluid.ts | 1727 ---------- .../superfluid/ISuperfluidGovernance.ts | 506 --- .../interfaces/superfluid/ISuperfluidToken.ts | 1010 ------ .../contracts/interfaces/superfluid/index.ts | 13 - .../interfaces/tokens/ERC20WithTokenInfo.ts | 384 --- .../contracts/interfaces/tokens/TokenInfo.ts | 109 - .../contracts/interfaces/tokens/index.ts | 5 - .../ethereum-contracts/index.ts | 5 - .../@superfluid-finance/index.ts | 5 - .../typechain-types/common.ts | 46 - .../@openzeppelin/contracts/index.ts | 5 - .../contracts/token/ERC20/IERC20__factory.ts | 206 -- .../IERC20Permit__factory.ts | 101 - .../draft-IERC20Permit.sol/index.ts | 4 - .../contracts/token/ERC20/extensions/index.ts | 4 - .../contracts/token/ERC20/index.ts | 5 - .../token/ERC721/IERC721__factory.ts | 311 -- .../extensions/IERC721Metadata__factory.ts | 356 --- .../token/ERC721/extensions/index.ts | 4 - .../contracts/token/ERC721/index.ts | 5 - .../token/ERC777/IERC777__factory.ts | 417 --- .../contracts/token/ERC777/index.ts | 4 - .../@openzeppelin/contracts/token/index.ts | 6 - .../@openzeppelin/contracts/utils/index.ts | 4 - .../utils/introspection/IERC165__factory.ts | 45 - .../contracts/utils/introspection/index.ts | 4 - .../factories/@openzeppelin/index.ts | 4 - .../contracts/apps/SuperAppBase__factory.ts | 275 -- .../contracts/apps/index.ts | 4 - .../ethereum-contracts/contracts/index.ts | 5 - .../IConstantFlowAgreementV1__factory.ts | 986 ------ .../contracts/interfaces/agreements/index.ts | 4 - .../contracts/interfaces/index.ts | 6 - .../superfluid/IConstantInflowNFT__factory.ts | 551 ---- .../IConstantOutflowNFT__factory.ts | 614 ---- .../superfluid/IFlowNFTBase__factory.ts | 515 --- .../superfluid/ISuperAgreement__factory.ts | 78 - .../superfluid/ISuperApp__factory.ts | 275 -- .../superfluid/ISuperTokenFactory__factory.ts | 276 -- .../superfluid/ISuperToken__factory.ts | 1839 ----------- .../ISuperfluidGovernance__factory.ts | 264 -- .../superfluid/ISuperfluidToken__factory.ts | 697 ---- .../superfluid/ISuperfluid__factory.ts | 1151 ------- .../contracts/interfaces/superfluid/index.ts | 13 - .../tokens/ERC20WithTokenInfo__factory.ts | 248 -- .../interfaces/tokens/TokenInfo__factory.ts | 65 - .../contracts/interfaces/tokens/index.ts | 5 - .../ethereum-contracts/index.ts | 4 - .../factories/@superfluid-finance/index.ts | 4 - .../typechain-types/factories/index.ts | 6 - .../src/VestingAutomation__factory.ts | 410 --- .../src/VestingScheduler__factory.ts | 938 ------ .../src/gelato/AutomateReady__factory.ts | 52 - .../gelato/AutomateTaskCreator__factory.ts | 96 - .../gelato/Types.sol/IAutomate__factory.ts | 129 - .../Types.sol/IOpsProxyFactory__factory.ts | 50 - .../ITaskTreasuryUpgradable__factory.ts | 76 - .../factories/src/gelato/Types.sol/index.ts | 6 - .../factories/src/gelato/index.ts | 6 - .../typechain-types/factories/src/index.ts | 7 - .../interface/IVestingScheduler__factory.ts | 537 ---- .../factories/src/interface/index.ts | 4 - .../typechain-types/hardhat.d.ts | 285 -- .../typechain-types/index.ts | 66 - .../typechain-types/src/VestingAutomation.ts | 592 ---- .../typechain-types/src/VestingScheduler.ts | 1160 ------- .../src/gelato/AutomateReady.ts | 104 - .../src/gelato/AutomateTaskCreator.ts | 187 -- .../src/gelato/Types.sol/IAutomate.ts | 222 -- .../src/gelato/Types.sol/IOpsProxyFactory.ts | 100 - .../Types.sol/ITaskTreasuryUpgradable.ts | 171 - .../src/gelato/Types.sol/index.ts | 6 - .../typechain-types/src/gelato/index.ts | 7 - .../typechain-types/src/index.ts | 9 - .../src/interface/IVestingScheduler.ts | 634 ---- .../typechain-types/src/interface/index.ts | 4 - .../types/custom.d.ts | 4 - .../.env.template | 22 - .../superfluid-gelato-automation/.gitignore | 7 - .../superfluid-gelato-automation/.gitmodules | 3 - .../.prettierignore | 22 - .../.prettierrc.yaml | 11 - .../superfluid-gelato-automation/.solcover.js | 3 - .../superfluid-gelato-automation/Makefile | 45 - .../superfluid-gelato-automation/README.md | 130 - .../build-and-test.sh | 18 - .../superfluid-gelato-automation/foundry.toml | 10 - .../hardhat.config.ts | 74 - .../lib/forge-std | 1 - .../superfluid-gelato-automation/package.json | 62 - .../remappings.txt | 3 - .../scripts/authorizeControl.ts | 52 - .../scripts/deploy.ts | 54 - .../scripts/deployFrameworkAndTokens.ts | 36 - .../scripts/verify.ts | 16 - .../src/SimpleACLCloseResolver.sol | 145 - .../src/interfaces/IResolver.sol | 12 - .../src/mocks/OpsMock.sol | 37 - .../src/test/SimpleACLCloseResolver.t.sol | 322 -- .../test/SimpleACLCloseResolver.test.ts | 336 -- .../tsconfig.json | 19 - .../types/custom.d.ts | 4 - projects/tradeable-cashflow/.env.template | 3 - projects/tradeable-cashflow/README.md | 45 - projects/tradeable-cashflow/arguments.js | 6 - .../cache/solidity-files-cache.json | 1478 --------- .../contracts/RedirectAll.sol | 183 -- .../contracts/TradeableCashflow.sol | 39 - .../deploy/deploy-tradeable-cashflow.js | 24 - projects/tradeable-cashflow/hardhat.config.js | 35 - projects/tradeable-cashflow/package.json | 34 - .../tradeable-cashflow/scripts/createFlow.js | 51 - .../tradeable-cashflow/scripts/deleteFlow.js | 45 - .../tradeable-cashflow/scripts/updateFlow.js | 45 - .../test/TradeableCashflow.test.js | 346 -- 286 files changed, 2 insertions(+), 53463 deletions(-) delete mode 100644 projects/borrow-against-salary/.env-template delete mode 100644 projects/borrow-against-salary/README.md delete mode 100644 projects/borrow-against-salary/cache/solidity-files-cache.json delete mode 100644 projects/borrow-against-salary/contracts/EmploymentLoan.sol delete mode 100644 projects/borrow-against-salary/contracts/LoanFactory.sol delete mode 100644 projects/borrow-against-salary/hardhat.config.js delete mode 100644 projects/borrow-against-salary/package.json delete mode 100644 projects/borrow-against-salary/scripts/createEmployerFlow.js delete mode 100644 projects/borrow-against-salary/scripts/deleteEmployerFlow.js delete mode 100644 projects/borrow-against-salary/scripts/deployFactory.js delete mode 100644 projects/borrow-against-salary/scripts/deployLoan.js delete mode 100644 projects/borrow-against-salary/scripts/getLoanById.js delete mode 100644 projects/borrow-against-salary/scripts/initialFunding.js delete mode 100644 projects/borrow-against-salary/scripts/lend.js delete mode 100644 projects/borrow-against-salary/scripts/lenderApproval.js delete mode 100644 projects/borrow-against-salary/scripts/updateEmployerFlow.js delete mode 100644 projects/borrow-against-salary/test/EmploymentLoan.test.js delete mode 100644 projects/flow-splitter/.gitignore delete mode 100644 projects/flow-splitter/README.md delete mode 100644 projects/flow-splitter/contracts/FlowSplitter.sol delete mode 100644 projects/flow-splitter/hardhat.config.js delete mode 100644 projects/flow-splitter/package.json delete mode 100644 projects/flow-splitter/resources/diagram.png delete mode 100644 projects/flow-splitter/scripts/deploy.js delete mode 100644 projects/flow-splitter/test/FlowSplitter.test.js delete mode 100644 projects/growing-nft/.env.template delete mode 100644 projects/growing-nft/.gitignore delete mode 100644 projects/growing-nft/README.md delete mode 100644 projects/growing-nft/arguments-flower-goerli.js delete mode 100644 projects/growing-nft/arguments-testtoken-goerli.js delete mode 100644 projects/growing-nft/contracts/Flower.sol delete mode 100644 projects/growing-nft/hardhat.config.js delete mode 100644 projects/growing-nft/package.json delete mode 100644 projects/growing-nft/resources/flower-metadatas/plant1.json delete mode 100644 projects/growing-nft/resources/flower-metadatas/plant2.json delete mode 100644 projects/growing-nft/resources/flower-metadatas/plant3.json delete mode 100644 projects/growing-nft/resources/img/flower-growth.png delete mode 100644 projects/growing-nft/resources/img/money-streaming-basic.gif delete mode 100644 projects/growing-nft/resources/img/plant-stages/plant1.png delete mode 100644 projects/growing-nft/resources/img/plant-stages/plant2.png delete mode 100644 projects/growing-nft/resources/img/plant-stages/plant3.png delete mode 100644 projects/growing-nft/resources/viz.pptx delete mode 100644 projects/growing-nft/scripts/deployFlower.js delete mode 100644 projects/growing-nft/scripts/mintSuperWater.js delete mode 100644 projects/growing-nft/scripts/streamWater.js delete mode 100644 projects/growing-nft/scripts/transferAway.js delete mode 100644 projects/growing-nft/scripts/utils/deployWater.js delete mode 100644 projects/growing-nft/scripts/utils/deployedContracts.js delete mode 100644 projects/growing-nft/scripts/utils/generateMetadata.js delete mode 100644 projects/growing-nft/test/Flower.test.js delete mode 100644 projects/instant-distribution-intro/.env.template delete mode 100644 projects/instant-distribution-intro/.eslintrc.js delete mode 100644 projects/instant-distribution-intro/.gitignore delete mode 100644 projects/instant-distribution-intro/.prettierignore delete mode 100644 projects/instant-distribution-intro/.solhint.json delete mode 100644 projects/instant-distribution-intro/.solhintignore delete mode 100644 projects/instant-distribution-intro/README.md delete mode 100644 projects/instant-distribution-intro/arguments-tokenspreader.js delete mode 100644 projects/instant-distribution-intro/contracts/TokenSpreader.sol delete mode 100644 projects/instant-distribution-intro/hardhat.config.js delete mode 100644 projects/instant-distribution-intro/package.json delete mode 100644 projects/instant-distribution-intro/resources/high_level_viz.png delete mode 100644 projects/instant-distribution-intro/scripts/cleanStatus.js delete mode 100644 projects/instant-distribution-intro/scripts/deleteShares.js delete mode 100644 projects/instant-distribution-intro/scripts/deploy.js delete mode 100644 projects/instant-distribution-intro/scripts/distribute.js delete mode 100644 projects/instant-distribution-intro/scripts/gainShare.js delete mode 100644 projects/instant-distribution-intro/scripts/loseShare.js delete mode 100644 projects/instant-distribution-intro/scripts/transferInSpreaderTokens.js delete mode 100644 projects/instant-distribution-intro/scripts/viewStatus.js delete mode 100644 projects/instant-distribution-intro/test/TokenSpreader.test.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/README.md delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/cache/solidity-files-cache.json delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/foundry.toml delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/.github/workflows/tests.yml delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/.gitignore delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/.gitmodules delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/LICENSE-APACHE delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/LICENSE-MIT delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/README.md delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/foundry.toml delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/.gitignore delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/LICENSE delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/Makefile delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/default.nix delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/demo/demo.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/lib/ds-test/src/test.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/package.json delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/Script.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/StdJson.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/Test.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/Vm.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/console.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/console2.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/Script.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/StdAssertions.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/StdCheats.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/StdError.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/StdMath.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/StdStorage.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/lib/forge-std/src/test/fixtures/broadcast.log.json delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/src/MoneyRouter.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-foundry/test/MoneyRouter.t.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/.env.example delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/README.md delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/cache/solidity-files-cache.json delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/contracts/MoneyRouter.sol delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/hardhat.config.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/package.json delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/aclApproval.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/createFlowFromContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/createFlowIntoContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/deleteFlowFromContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/deleteFlowIntoContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/deploy.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/sendLumpSumToContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/tokenApproval.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/updateFlowFromContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/updateFlowIntoContract.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/scripts/withdrawFunds.js delete mode 100644 projects/money-streaming-intro/money-streaming-intro-hardhat/test/MoneyRouter.test.js delete mode 100644 projects/superfluid-automated-vesting/.env.template delete mode 100644 projects/superfluid-automated-vesting/README.md delete mode 100755 projects/superfluid-automated-vesting/build-and-test.sh delete mode 100644 projects/superfluid-automated-vesting/cache/solidity-files-cache.json delete mode 100644 projects/superfluid-automated-vesting/hardhat.config.ts delete mode 100644 projects/superfluid-automated-vesting/package.json delete mode 100644 projects/superfluid-automated-vesting/scripts/Helpers.ts delete mode 100644 projects/superfluid-automated-vesting/scripts/createVestingSchedule.ts delete mode 100644 projects/superfluid-automated-vesting/scripts/createVestingTask.ts delete mode 100644 projects/superfluid-automated-vesting/scripts/deployVestingAutomation.ts delete mode 100644 projects/superfluid-automated-vesting/scripts/deployVestingScheduler.ts delete mode 100644 projects/superfluid-automated-vesting/src/VestingAutomation.sol delete mode 100644 projects/superfluid-automated-vesting/src/VestingScheduler.sol delete mode 100644 projects/superfluid-automated-vesting/src/gelato/AutomateReady.sol delete mode 100644 projects/superfluid-automated-vesting/src/gelato/AutomateTaskCreator.sol delete mode 100644 projects/superfluid-automated-vesting/src/gelato/Types.sol delete mode 100644 projects/superfluid-automated-vesting/src/interface/IVestingScheduler.sol delete mode 100644 projects/superfluid-automated-vesting/test/VestingAutomation.test.ts delete mode 100644 projects/superfluid-automated-vesting/tsconfig.json delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC20/IERC20.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol/IERC20Permit.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC20/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC721/IERC721.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC721/extensions/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC721/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC777/IERC777.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/ERC777/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/token/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/utils/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/utils/introspection/IERC165.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/contracts/utils/introspection/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@openzeppelin/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/apps/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantInflowNFT.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantOutflowNFT.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IFlowNFTBase.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperAgreement.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperApp.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperTokenFactory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidToken.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ERC20WithTokenInfo.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/TokenInfo.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/ethereum-contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/@superfluid-finance/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/common.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC20/IERC20__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol/IERC20Permit__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC20/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC721/IERC721__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC721/extensions/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC721/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC777/IERC777__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/ERC777/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/token/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/utils/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/utils/introspection/IERC165__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/contracts/utils/introspection/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@openzeppelin/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/apps/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantInflowNFT__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantOutflowNFT__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IFlowNFTBase__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperAgreement__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperApp__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperTokenFactory__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidToken__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ERC20WithTokenInfo__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/TokenInfo__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/ethereum-contracts/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/@superfluid-finance/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/VestingAutomation__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/VestingScheduler__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/AutomateReady__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/AutomateTaskCreator__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/Types.sol/IAutomate__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/Types.sol/IOpsProxyFactory__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/Types.sol/ITaskTreasuryUpgradable__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/Types.sol/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/gelato/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/interface/IVestingScheduler__factory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/factories/src/interface/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/hardhat.d.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/VestingAutomation.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/VestingScheduler.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/AutomateReady.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/AutomateTaskCreator.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/Types.sol/IAutomate.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/Types.sol/IOpsProxyFactory.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/Types.sol/ITaskTreasuryUpgradable.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/Types.sol/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/gelato/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/index.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/interface/IVestingScheduler.ts delete mode 100644 projects/superfluid-automated-vesting/typechain-types/src/interface/index.ts delete mode 100644 projects/superfluid-automated-vesting/types/custom.d.ts delete mode 100644 projects/superfluid-gelato-automation/.env.template delete mode 100644 projects/superfluid-gelato-automation/.gitignore delete mode 100644 projects/superfluid-gelato-automation/.gitmodules delete mode 100644 projects/superfluid-gelato-automation/.prettierignore delete mode 100644 projects/superfluid-gelato-automation/.prettierrc.yaml delete mode 100644 projects/superfluid-gelato-automation/.solcover.js delete mode 100644 projects/superfluid-gelato-automation/Makefile delete mode 100644 projects/superfluid-gelato-automation/README.md delete mode 100755 projects/superfluid-gelato-automation/build-and-test.sh delete mode 100644 projects/superfluid-gelato-automation/foundry.toml delete mode 100644 projects/superfluid-gelato-automation/hardhat.config.ts delete mode 160000 projects/superfluid-gelato-automation/lib/forge-std delete mode 100644 projects/superfluid-gelato-automation/package.json delete mode 100644 projects/superfluid-gelato-automation/remappings.txt delete mode 100644 projects/superfluid-gelato-automation/scripts/authorizeControl.ts delete mode 100644 projects/superfluid-gelato-automation/scripts/deploy.ts delete mode 100644 projects/superfluid-gelato-automation/scripts/deployFrameworkAndTokens.ts delete mode 100644 projects/superfluid-gelato-automation/scripts/verify.ts delete mode 100644 projects/superfluid-gelato-automation/src/SimpleACLCloseResolver.sol delete mode 100644 projects/superfluid-gelato-automation/src/interfaces/IResolver.sol delete mode 100644 projects/superfluid-gelato-automation/src/mocks/OpsMock.sol delete mode 100644 projects/superfluid-gelato-automation/src/test/SimpleACLCloseResolver.t.sol delete mode 100644 projects/superfluid-gelato-automation/test/SimpleACLCloseResolver.test.ts delete mode 100644 projects/superfluid-gelato-automation/tsconfig.json delete mode 100644 projects/superfluid-gelato-automation/types/custom.d.ts delete mode 100644 projects/tradeable-cashflow/.env.template delete mode 100644 projects/tradeable-cashflow/README.md delete mode 100644 projects/tradeable-cashflow/arguments.js delete mode 100644 projects/tradeable-cashflow/cache/solidity-files-cache.json delete mode 100644 projects/tradeable-cashflow/contracts/RedirectAll.sol delete mode 100644 projects/tradeable-cashflow/contracts/TradeableCashflow.sol delete mode 100644 projects/tradeable-cashflow/deploy/deploy-tradeable-cashflow.js delete mode 100644 projects/tradeable-cashflow/hardhat.config.js delete mode 100644 projects/tradeable-cashflow/package.json delete mode 100644 projects/tradeable-cashflow/scripts/createFlow.js delete mode 100644 projects/tradeable-cashflow/scripts/deleteFlow.js delete mode 100644 projects/tradeable-cashflow/scripts/updateFlow.js delete mode 100644 projects/tradeable-cashflow/test/TradeableCashflow.test.js diff --git a/README.md b/README.md index 1ecaf76..2e8d577 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ Each example has their own dependencies. This is to reduce complexity when using one or a small number of examples from this repository. To get started with any exmaple project, navigate to the appropriate example -directory and view the "Installation" section of its README file. Most projects -use `npm`, but others may use `forge` or another tool. +directory and view the "Installation" section of its README file. ## New Example Requests @@ -26,8 +25,4 @@ example, open an issue and select the "New Example" template. The following is a list of example projects in this repository. Follow the link on each to see their local README file. -- [Borrow Against Salary](./projects/borrow-against-salary/README.md) -- [Instant Distribution Intro](./projects/instant-distribution-intro/README.md) -- [Money Streaming Intro](./projects/money-streaming-intro) -- [Tradeable Cashflow NFT](./projects/tradeable-cashflow/README.md) -- [Gelato ACL Automation](./projects/superfluid-gelato-automation/) +- [Gda Advertisement Auction](./projects/gda-advertisement-auction/README.md) diff --git a/projects/borrow-against-salary/.env-template b/projects/borrow-against-salary/.env-template deleted file mode 100644 index 92272b9..0000000 --- a/projects/borrow-against-salary/.env-template +++ /dev/null @@ -1,4 +0,0 @@ -GOERLI_URL= -EMPLOYER_PRIVATE_KEY= -BORROWER_PRIVATE_KEY= -LENDER_PRIVATE_KEY= diff --git a/projects/borrow-against-salary/README.md b/projects/borrow-against-salary/README.md deleted file mode 100644 index c712d31..0000000 --- a/projects/borrow-against-salary/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# Borrowing against a Superfluid salary stream - -This example is a base implementation of a loan contract which is tied to a salary stream. - -### The application assumes 3 parties: - -1. Employer - likely one who is whitelisted or a trustworthy employer -2. Borrower - an employee currently receiving a salary stream from the Employer -3. Lender - an outside party who lends to the Borrower - -### There are several steps involved in the origination of each loan contract: - -1. The `Borrower` deploys a new `EmploymentLoan.sol` contract, and passes the following into the constructor: - -- borrow amount -- the desired interest rate -- the duration of the loan -- employer address -- the borrower's own address -- the token to be borrowed -- address of the Superfluid host contract on the current network - -2. Once the contract is deployed, the borrower will instruct their employer to begin sending 100% of their salary into the contract. Because the application is a Super App, our contract will to run the `afterAgreementCreated` callback, where we have inserted logic which will initially send 100% of the incoming flow to the borrower. In this case, the borrower will still be receiving 100% of their salary. - -3. A lender may call `approve()` on the borrowToken contract, passing in the address of the current employmentLoan contract as the spender. From there, the lender may call the `lend()` function to enter into a loan agreement with the borrower. The lend function will ensure the employer is indeed streaming money into the contract. If this check passes, then the lent funds will be sent to the borrower, and the flow from the contract to the employee will be split so that the lender is now receiving a streaming interest payment which is taken directly from the borrower's salary. The borrower's incoming salary stream is now equal to 100% of the previous salary amount - the lender's interest rate stream amount. - -4. If the employer stops the flow rate into the employment loan contract, then the flow from the contract to the lender and borrower is stopped by making use of the `afterAgreementTerminated` callback. If the contract at any point starts once again receiving a stream in the borrow token which is enough to pay for the lender's interest rate payments, a new stream in the borrow token is started to both the lender and borrower once again as in step 3. The contract will note that the loan is still open, so the lender will begin receiving their interest once again. - -5. Once a loan is completed, the borrower may call closeLoan() to stop the stream to the lender, and once again receive 100% of their salary stream. A loan can be terminated early at any time by the borrower if the borrower is willing to pay off the remainder of the loan in a single transaction. - -### Opportunities for Further Work - -1. There is no collateral mechanism here, which prevents this from being done in a fully permissionless and 100% secure way. You can see the example shown in this workshop for an implementation that requires collateral to be locked by the borrower: https://www.youtube.com/watch?v=yxzOimYwxHY -2. The Loan contract is not tradeable for the lender. It would be interesting to mint the lender a tradeable cashflow NFT to represent their rights to interest payment streams so that these loans may be traded on the secondary market. -3. The borrower needs to manually call closeLoan() to terminate the stream to the lender once the loan has been completed. It would be worth using a Keeper to ensure that the loan is closed as quickly as possible upon completion. -4. This system is peer to peer, and would be hard to scale by itself. How could you build a system that generalizes this using lending pools that can be drawn from instead? diff --git a/projects/borrow-against-salary/cache/solidity-files-cache.json b/projects/borrow-against-salary/cache/solidity-files-cache.json deleted file mode 100644 index c79d1df..0000000 --- a/projects/borrow-against-salary/cache/solidity-files-cache.json +++ /dev/null @@ -1,990 +0,0 @@ -{ - "_format": "hh-sol-cache-2", - "files": { - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\contracts\\EmploymentLoan.sol": { - "lastModificationDate": 1683836703927, - "contentHash": "8feac0cbf4bf69f79000262d7e467a53", - "sourceName": "contracts/EmploymentLoan.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol", - "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol", - "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBaseCFA.sol" - ], - "versionPragmas": [ - ">=0.8.0" - ], - "artifacts": [ - "EmploymentLoan" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\apps\\SuperTokenV1Library.sol": { - "lastModificationDate": 1683730720817, - "contentHash": "0c86e0e3f51e35daf4d0927725367c4a", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../interfaces/superfluid/ISuperfluid.sol", - "../interfaces/agreements/IConstantFlowAgreementV1.sol", - "../interfaces/agreements/IInstantDistributionAgreementV1.sol" - ], - "versionPragmas": [ - ">= 0.8.0" - ], - "artifacts": [ - "SuperTokenV1Library" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\apps\\SuperAppBaseCFA.sol": { - "lastModificationDate": 1683818648884, - "contentHash": "afb644e24e5605d732849c1ee2d83312", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBaseCFA.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../interfaces/superfluid/ISuperfluid.sol", - "./SuperTokenV1Library.sol" - ], - "versionPragmas": [ - ">= 0.8.0" - ], - "artifacts": [ - "SuperAppBaseCFA" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperfluid.sol": { - "lastModificationDate": 1683730720732, - "contentHash": "923d8bee2334395f7ad909b8e98e07e9", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperfluidGovernance.sol", - "./ISuperfluidToken.sol", - "./ISuperToken.sol", - "./ISuperTokenFactory.sol", - "./ISuperAgreement.sol", - "./ISuperApp.sol", - "./Definitions.sol", - "../tokens/TokenInfo.sol", - "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "@openzeppelin/contracts/token/ERC777/IERC777.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperfluid" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\agreements\\IConstantFlowAgreementV1.sol": { - "lastModificationDate": 1683730720677, - "contentHash": "253f76f0473cf1297607b03447c4b53e", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../superfluid/ISuperAgreement.sol", - "../superfluid/ISuperfluidToken.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "IConstantFlowAgreementV1" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\agreements\\IInstantDistributionAgreementV1.sol": { - "lastModificationDate": 1683730720693, - "contentHash": "60042f78e0fa54a8547790c391ab27c1", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IInstantDistributionAgreementV1.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../superfluid/ISuperAgreement.sol", - "../superfluid/ISuperfluidToken.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "IInstantDistributionAgreementV1" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperfluidGovernance.sol": { - "lastModificationDate": 1683730720736, - "contentHash": "f39a1c0568c38958f3c5c80b6fb1603c", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperAgreement.sol", - "./ISuperToken.sol", - "./ISuperfluidToken.sol", - "./ISuperfluid.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperfluidGovernance" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@openzeppelin\\contracts\\token\\ERC777\\IERC777.sol": { - "lastModificationDate": 1682695447813, - "contentHash": "35b56a00de0fb16f9e6a8d7464d31b0b", - "sourceName": "@openzeppelin/contracts/token/ERC777/IERC777.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - "^0.8.0" - ], - "artifacts": [ - "IERC777" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperTokenFactory.sol": { - "lastModificationDate": 1683730720758, - "contentHash": "e49894d55d38f583c443600c4c99f8c6", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperTokenFactory.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperToken.sol", - "../tokens/ERC20WithTokenInfo.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperTokenFactory" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperfluidToken.sol": { - "lastModificationDate": 1683730720752, - "contentHash": "095cd6cdf7df13a739649f8917862cb0", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidToken.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperAgreement.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperfluidToken" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperToken.sol": { - "lastModificationDate": 1683730720758, - "contentHash": "61469de32bea27827319123c4a735dab", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperfluid.sol", - "./ISuperfluidToken.sol", - "../tokens/TokenInfo.sol", - "@openzeppelin/contracts/token/ERC777/IERC777.sol", - "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "./IConstantOutflowNFT.sol", - "./IConstantInflowNFT.sol", - "./IPoolAdminNFT.sol", - "./IPoolMemberNFT.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperToken" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\tokens\\TokenInfo.sol": { - "lastModificationDate": 1683730720838, - "contentHash": "6326411b7eb3d451fdffd149d821a7cb", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/TokenInfo.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "TokenInfo" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@openzeppelin\\contracts\\token\\ERC20\\IERC20.sol": { - "lastModificationDate": 1682695447813, - "contentHash": "ad7c2d0af148c8f9f097d65deeb4da6b", - "sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - "^0.8.0" - ], - "artifacts": [ - "IERC20" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperApp.sol": { - "lastModificationDate": 1683730720726, - "contentHash": "5057526e30b17d708447527cb6485d6f", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperApp.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperToken.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperApp" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\ISuperAgreement.sol": { - "lastModificationDate": 1683730720718, - "contentHash": "49d978f06d4edeb5125a7152d6897e69", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperAgreement.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperfluidToken.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ISuperAgreement" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\Definitions.sol": { - "lastModificationDate": 1683730720644, - "contentHash": "d6576f4814532782fca96c1bd26db384", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/Definitions.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "BatchOperation", - "ContextDefinitions", - "FlowOperatorDefinitions", - "SuperAppDefinitions", - "SuperfluidGovernanceConfigs" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\IConstantOutflowNFT.sol": { - "lastModificationDate": 1683730720677, - "contentHash": "d68a7fe94e12d2277eedd4054fc59a6b", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantOutflowNFT.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperfluidToken.sol", - "./IFlowNFTBase.sol" - ], - "versionPragmas": [ - ">=0.8.4" - ], - "artifacts": [ - "IConstantOutflowNFT" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\IPoolAdminNFT.sol": { - "lastModificationDate": 1683730720702, - "contentHash": "6a5ee5e8ea02a99a32948c3ba8f750a8", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IPoolAdminNFT.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - ">=0.8.4" - ], - "artifacts": [ - "IPoolAdminNFT" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\IConstantInflowNFT.sol": { - "lastModificationDate": 1683730720677, - "contentHash": "50e7ff0bc0e1235a45c52de2eae4df43", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IConstantInflowNFT.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "./ISuperToken.sol", - "./IFlowNFTBase.sol" - ], - "versionPragmas": [ - ">=0.8.4" - ], - "artifacts": [ - "IConstantInflowNFT" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\IPoolMemberNFT.sol": { - "lastModificationDate": 1683730720702, - "contentHash": "71ace2838abebb46b969d54537e4458d", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IPoolMemberNFT.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - ">=0.8.4" - ], - "artifacts": [ - "IPoolMemberNFT" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\tokens\\ERC20WithTokenInfo.sol": { - "lastModificationDate": 1683730720654, - "contentHash": "77968d7579793b9f87e33dec4d917f44", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ERC20WithTokenInfo.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "@openzeppelin/contracts/token/ERC20/IERC20.sol", - "./TokenInfo.sol" - ], - "versionPragmas": [ - ">= 0.8.4" - ], - "artifacts": [ - "ERC20WithTokenInfo" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@superfluid-finance\\ethereum-contracts\\contracts\\interfaces\\superfluid\\IFlowNFTBase.sol": { - "lastModificationDate": 1683730720687, - "contentHash": "10e54273c5b21ca0b88131952611bf26", - "sourceName": "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/IFlowNFTBase.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol", - "./ISuperToken.sol" - ], - "versionPragmas": [ - ">=0.8.4" - ], - "artifacts": [ - "IFlowNFTBase" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@openzeppelin\\contracts\\token\\ERC721\\extensions\\IERC721Metadata.sol": { - "lastModificationDate": 1682695447813, - "contentHash": "efbc0d15b80a74e34dbe8da0f3e879bb", - "sourceName": "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../IERC721.sol" - ], - "versionPragmas": [ - "^0.8.0" - ], - "artifacts": [ - "IERC721Metadata" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@openzeppelin\\contracts\\token\\ERC721\\IERC721.sol": { - "lastModificationDate": 1682695447813, - "contentHash": "eb7e61db29f31d88b3c1cef1b063d338", - "sourceName": "@openzeppelin/contracts/token/ERC721/IERC721.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "../../utils/introspection/IERC165.sol" - ], - "versionPragmas": [ - "^0.8.0" - ], - "artifacts": [ - "IERC721" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\node_modules\\@openzeppelin\\contracts\\utils\\introspection\\IERC165.sol": { - "lastModificationDate": 1682695447797, - "contentHash": "03e6768535ac4da0e9756f1d8a4a018a", - "sourceName": "@openzeppelin/contracts/utils/introspection/IERC165.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [], - "versionPragmas": [ - "^0.8.0" - ], - "artifacts": [ - "IERC165" - ] - }, - "C:\\Users\\Joel Jesudason\\Documents\\Superfluid\\main\\super-examples\\projects\\borrow-against-salary\\contracts\\LoanFactory.sol": { - "lastModificationDate": 1683813275785, - "contentHash": "8cb81f60a1a10e3635d86eb606c68233", - "sourceName": "contracts/LoanFactory.sol", - "solcConfig": { - "version": "0.8.14", - "settings": { - "optimizer": { - "enabled": false, - "runs": 200 - }, - "outputSelection": { - "*": { - "*": [ - "abi", - "evm.bytecode", - "evm.deployedBytecode", - "evm.methodIdentifiers", - "metadata" - ], - "": [ - "ast" - ] - } - } - } - }, - "imports": [ - "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol", - "./EmploymentLoan.sol" - ], - "versionPragmas": [ - ">=0.8.0" - ], - "artifacts": [ - "LoanFactory" - ] - } - } -} diff --git a/projects/borrow-against-salary/contracts/EmploymentLoan.sol b/projects/borrow-against-salary/contracts/EmploymentLoan.sol deleted file mode 100644 index dd3f142..0000000 --- a/projects/borrow-against-salary/contracts/EmploymentLoan.sol +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {ISuperfluid, ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; - -import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; - -import {SuperAppBaseFlow} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBaseFlow.sol"; - -/// @title Employment Loan Contract -/// @author Superfluid -contract EmploymentLoan is SuperAppBaseFlow { - - /// @notice Importing the SuperToken Library to make working with streams easy. - using SuperTokenV1Library for ISuperToken; - // --------------------------------------------------------------------------------------------- - // STORAGE & IMMUTABLES - - /// @notice Total amount borrowed. - int256 public immutable borrowAmount; - - /// @notice Interest rate, in whole number. I.e. 8% interest rate would be passed as '8' - int8 public immutable interestRate; - - /// @notice Number of months the loan will be paid back in. I.e. 2 years = '24' - int256 public immutable paybackMonths; - - /// @notice Address of employer - must be allow-listed for this example - address public immutable employer; - - /// @notice Borrower address. - address public immutable borrower; - - /// @notice Token being borrowed. - ISuperToken public immutable borrowToken; - - /// @notice Lender address. - address public lender; - - /// @notice boolean flag to track whether or not the loan is open - bool public loanOpen; - - /// @notice Timestamp of the loan start time. - uint256 public loanStartTime; - - /// @notice boolean flag to track whether the loan was closed. In contrary to @loanOpen, this variable changes state only once. - bool public isClosed; - - // --------------------------------------------------------------------------------------------- - //MODIFIERS - - ///@dev checks that only the borrowToken is used when sending streams into this contract - ///@param superToken the token being streamed into the contract - function isAcceptedSuperToken(ISuperToken superToken) public view override returns (bool) { - return address(superToken) == address(borrowToken); - } - - constructor( - int256 _borrowAmount, // amount to be borrowed - int8 _interestRate, // annual interest rate, in whole number - i.e. 8% would be passed as 8 - int256 _paybackMonths, // total payback months - address _employer, // allow-listed employer address - address _borrower, // borrower address - ISuperToken _borrowToken, // super token to be used in borrowing - ISuperfluid _host // address of SF host - ) SuperAppBaseFlow( - _host, - true, - true, - true - ) { - borrowAmount = _borrowAmount; - interestRate = _interestRate; - paybackMonths = _paybackMonths; - employer = _employer; - borrower = _borrower; - borrowToken = _borrowToken; - host = _host; - loanOpen = false; - isClosed = false; - } - - /// @dev Calculates the flow rate to be sent to the lender to repay the stream. - /// @return paymentFlowRate The flow rate to be paid to the lender. - function getPaymentFlowRate() public view returns (int96 paymentFlowRate) { - return ( - int96( - ((borrowAmount + ((borrowAmount * int256(interestRate)) / int256(100))) / - paybackMonths) / ((365 / 12) * 86400) - //365/12 = average days in a month; 86400 = seconds in a day (24 hours); -> 365/12 * 86400 average seconds in a month - ) - ); - } - - // --------------------------------------------------------------------------------------------- - // FUNCTIONS & CORE LOGIC - - /// @notice Get the total amount of super tokens that the borrower still needs to repay on the - /// loan. - /// @return Total number of remaining tokens to be paid on the loan in wei used to calculate - /// whether or not a loan may be closed. - function getTotalAmountRemaining() public view returns (uint256) { - //if there is no time left on loan, return zero - int256 secondsLeft = (paybackMonths * int256((365 * 86400) / 12)) - - int256(block.timestamp - loanStartTime); - if (secondsLeft <= 0) { - return 0; - } else { - //if an amount is left, return the total amount to be paid - return uint256(secondsLeft) * uint256(int256(getPaymentFlowRate())); - } - } - - /// @notice lender can use this function to send funds to the borrower and start the loan - /// @dev function also handles the splitting of flow to lender - function lend() external { - int96 employerFlowRate = borrowToken.getFlowRate(employer, address(this)); - - require(!isClosed, "Loan already closed"); - require(employerFlowRate >= getPaymentFlowRate(), "insufficient flowRate"); - - //lender must approve contract before running next line - borrowToken.transferFrom(msg.sender, borrower, uint256(borrowAmount)); - - //want to make sure that tokens are sent successfully first before setting lender to msg.sender - int96 netFlowRate = borrowToken.getNetFlowRate(address(this)); - - int96 outFlowRate = borrowToken.getFlowRate(address(this), borrower); - - //update flow to borrower (aka the employee) - borrowToken.updateFlow( - borrower, - ((netFlowRate - outFlowRate) * -1) - getPaymentFlowRate() - ); - - //create flow to lender - borrowToken.createFlow(msg.sender, getPaymentFlowRate()); - - loanOpen = true; - lender = msg.sender; - loanStartTime = block.timestamp; - } - - /// @notice handle the case of a stream being created into the contract - /// @param ctx the context value passed into updateOutflow in super app callbacks - /// @param paymentFlowRate the flow rate to be sent to the lender if a loan were to activate - /// (this could be the same value as outFlowRate) - /// @param inFlowRate the flow rate sent into the contract from the employer - /// used within the _updateOutflow function which is ultimately called in the callbacks - function _updateOutFlowCreate( - bytes calldata ctx, - int96 paymentFlowRate, - int96 inFlowRate - ) private returns (bytes memory newCtx) { - newCtx = ctx; - //get the current sender of the flow - address sender = host.decodeCtx(ctx).msgSender; - //this will revert and no outflow or inflow will be created if the sender of the flow is not - // the emploer - require(sender == employer, "sender of flow must be the employer"); - // @dev If there is no existing outflow, then create new flow to equal inflow - // sender must also be the employer - //create flow to employee - //if loan is still open, we need to make sure that the right amount of funds are sent to the - // borrower & lender - if (loanOpen == true) { - newCtx = borrowToken.createFlowWithCtx( - borrower, - inFlowRate - paymentFlowRate, - newCtx - ); - newCtx = borrowToken.createFlowWithCtx(lender, paymentFlowRate, newCtx); - } else { - //if loanOpen is not true, we need to send the borrower the full inflow - newCtx = borrowToken.createFlowWithCtx(borrower, inFlowRate, newCtx); - } - } - - /// @dev manages edge cases related to flow updates - /// to be used within _updateOutflow function - /// @param ctx context passed by super app callback - /// @param paymentFlowRate the flow rate to be sent to the lender if a loan were to activate - /// (this could be the same value as outFlowRate) - /// @param outFlowRateLender the flow rate being sent to lender from the contract - /// @param inFlowRate the flow rate sent into the contract from the employer - /// @dev if flowrate into the contract is enough to cover loan repayment, then just update - /// outflow to borrower. if flowrate into contract is not enough to cover loan repayment, we - /// need to ensure that the lender gets everything going into the contract - function _updateOutFlowUpdate( - bytes calldata ctx, - int96 paymentFlowRate, - int96 outFlowRateLender, - int96 inFlowRate - ) private returns (bytes memory newCtx) { - newCtx = ctx; - // this will get us the amount of money that should be redirected to the lender out of the - // inflow, denominated in borrow token - - int96 borrowerInFlow = borrowToken.getFlowRate(address(this), borrower); - - //if the amount being sent is enough to cover loan - if ((inFlowRate - paymentFlowRate) > 0) { - //if there is currently an outflow to the lender - if (outFlowRateLender > 0) { - //if the borrower is receiving money - if (borrowerInFlow > 0) { - newCtx = borrowToken.updateFlowWithCtx( - borrower, - inFlowRate - paymentFlowRate, - newCtx - ); - } else { - newCtx = borrowToken.createFlowWithCtx( - borrower, - inFlowRate - paymentFlowRate, - newCtx - ); - } - newCtx = borrowToken.updateFlowWithCtx(lender, paymentFlowRate, newCtx); - } else { - newCtx = borrowToken.updateFlowWithCtx(borrower, inFlowRate, newCtx); - } - // the following case is here because the lender will be paid first - // if there's not enough money to pay off the loan in full, the lender gets paid - // everything coming in to the contract - } else if ((inFlowRate - paymentFlowRate <= 0) && inFlowRate > 0) { - // if inFlowRate is less than the required amount to pay interest, but there's still a - // flow, we'll stream it all to the lender - if (outFlowRateLender > 0) { - newCtx = borrowToken.deleteFlowWithCtx(address(this), borrower, newCtx); - newCtx = borrowToken.updateFlowWithCtx(lender, inFlowRate, newCtx); - } else { - //in this case, there is no lender outFlowRate..so we need to just update the outflow to borrower - newCtx = borrowToken.updateFlowWithCtx(borrower, inFlowRate, newCtx); - } - } - } - - /// @notice handles deletion of flow into contract - /// @dev ensures that streams sent out of the contract are also stopped - /// @param ctx context passed by super app callback - /// @param outFlowRateLender the flow rate being sent to lender from the contract - function _updateOutFlowDelete(bytes calldata ctx, int96 outFlowRateLender) - private - returns (bytes memory newCtx) - { - newCtx = ctx; - // delete flow to lender in borrow token if they are currently receiving a flow - if (outFlowRateLender > 0) { - newCtx = borrowToken.deleteFlowWithCtx(address(this), lender, newCtx); - } - // delete flow to borrower in borrow token - newCtx = borrowToken.deleteFlowWithCtx(address(this), borrower, newCtx); - } - - /// @notice handles create, update, and delete case - to be run in each callback - /// @param ctx context passed by super app callback - function _updateOutflow(bytes calldata ctx) private returns (bytes memory newCtx) { - newCtx = ctx; - //this will get us the amount of money that should be redirected to the lender out of the inflow, denominated in borrow token - int96 paymentFlowRate = getPaymentFlowRate(); - // @dev This will give me the new flowRate, as it is called in after callbacks - int96 netFlowRate = borrowToken.getNetFlowRate(address(this)); - - //current amount being sent to lender - int96 outFlowRateLender = borrowToken.getFlowRate(address(this), lender); - //current amount being sent to borrower - int96 outFlowRateBorrower = borrowToken.getFlowRate(address(this), borrower); - //total outflow rate in borrow token - only 2 - int96 outFlowRate = outFlowRateLender + outFlowRateBorrower; - //total inflow rate in borrow token - int96 inFlowRate = netFlowRate + outFlowRate; - - if (inFlowRate < 0) { - inFlowRate = inFlowRate * -1; // Fixes issue when inFlowRate is negative - } - - // @dev If inFlow === 0 && outflowRate > 0, then delete existing flows. - if (inFlowRate == int96(0)) { - newCtx = _updateOutFlowDelete(ctx, outFlowRateLender); - } - //if flow exists, update the flow according to various params - else if (outFlowRate != int96(0)) { - newCtx = _updateOutFlowUpdate(ctx, paymentFlowRate, outFlowRateLender, inFlowRate); - } - //no flow exists into the contract in borrow token - else { - newCtx = _updateOutFlowCreate(ctx, paymentFlowRate, inFlowRate); - // @dev If there is no existing outflow, then create new flow to equal inflow - } - } - - /// @notice function to close a loan that is already completed - function closeCompletedLoan() external { - require(getTotalAmountRemaining() <= 0); - - int96 currentLenderFlowRate = borrowToken.getFlowRate(address(this), lender); - borrowToken.deleteFlow(address(this), lender); - - int96 currentFlowRate = borrowToken.getFlowRate(address(this), borrower); - borrowToken.updateFlow(borrower, currentFlowRate + currentLenderFlowRate); - loanOpen = false; - isClosed = true; - } - - ///@notice allows lender or borrower to close a loan that is not yet finished - ///@param amountForPayoff the amount to be paid right now to close the loan in wei - /// @dev if the loan is paid off, or if the loan is closed by the lender, pass 0. if the loan is - /// not yet paid off, pass in the required amount to close loan - function closeOpenLoan(uint256 amountForPayoff) external { - int96 currentLenderFlowRate = borrowToken.getFlowRate(address(this), lender); - int96 currentFlowRate = borrowToken.getFlowRate(address(this), borrower); - - // lender may close the loan early to forgive the debt - if (msg.sender == lender) { - borrowToken.deleteFlow(address(this), lender); - borrowToken.updateFlow(borrower, currentFlowRate + currentLenderFlowRate); - loanOpen = false; - isClosed = true; - } else { - require(amountForPayoff >= (getTotalAmountRemaining()), "insuf funds"); - require(getTotalAmountRemaining() > 0, "you should call closeOpenLoan() instead"); - borrowToken.transferFrom(msg.sender, lender, amountForPayoff); - - borrowToken.deleteFlow(address(this), lender); - - borrowToken.updateFlow(borrower, currentFlowRate + currentLenderFlowRate); - loanOpen = false; - isClosed = true; - } - } - - // --------------------------------------------------------------------------------------------- - // SUPER APP CALLBACKS - - /// @dev super app after flow created callback - function onFlowCreated( - ISuperToken /*superToken*/, - address /*sender*/, - bytes calldata ctx - ) - internal - override - returns (bytes memory newCtx) - { - newCtx = _updateOutflow(ctx); - } - - /// @dev super app after flow updated callback - function onFlowUpdated( - ISuperToken /*superToken*/, - address /*sender*/, - int96 /*previousFlowRate*/, - uint256 /*lastUpdated*/, - bytes calldata ctx - ) - internal - override - returns (bytes memory newCtx) - { - newCtx = _updateOutflow(ctx); - } - - /// @dev super app after flow deleted callback - function onFlowDeleted( - ISuperToken /*superToken*/, - address /*sender*/, - address /*receiver*/, - int96 /*previousFlowRate*/, - uint256 /*lastUpdated*/, - bytes calldata ctx - ) - internal - override - returns (bytes memory newCtx) - { - newCtx = _updateOutflow(ctx); - } -} \ No newline at end of file diff --git a/projects/borrow-against-salary/contracts/LoanFactory.sol b/projects/borrow-against-salary/contracts/LoanFactory.sol deleted file mode 100644 index 51a59b5..0000000 --- a/projects/borrow-against-salary/contracts/LoanFactory.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.8.0; - -import {ISuperfluid, ISuperToken, ISuperApp, ISuperAgreement, SuperAppDefinitions} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; - -import {EmploymentLoan} from "./EmploymentLoan.sol"; - -contract LoanFactory { - /// @notice counter which is iterated +1 for each new loan created. - /// @dev Note that the value begins at 0 here, but the first one will start at one. - uint256 public loanId; - - /// @notice mapping of loanId to the loan contract - mapping(uint256 => EmploymentLoan) public idToLoan; - - /// @notice mapping of loan owner (i.e. the msg.sender on the call) to the loan Id - mapping(address => uint256) public employmentLoanOwners; - - /// @notice Creates new loan contract. - /// @param _borrowAmount Amount to borrow. - /// @param _interestRate Interest rate. - /// @param _paybackMonths Number of months for repayment. - /// @param _employer Employer address. - /// @param _borrower Borrower address. - /// @param _borrowToken Token to borrow. - /// @param _host Superfluid host. - /// @return Loan ID. - function createNewLoan( - int256 _borrowAmount, - int8 _interestRate, - int8 _paybackMonths, - address _employer, - address _borrower, - ISuperToken _borrowToken, - ISuperfluid _host - ) external returns (uint256) { - EmploymentLoan newLoan = new EmploymentLoan( - _borrowAmount, - _interestRate, - _paybackMonths, - _employer, - _borrower, - _borrowToken, - _host - ); - - loanId++; - - idToLoan[loanId] = newLoan; - employmentLoanOwners[msg.sender] = loanId; - - return loanId; - } -} diff --git a/projects/borrow-against-salary/hardhat.config.js b/projects/borrow-against-salary/hardhat.config.js deleted file mode 100644 index a28160a..0000000 --- a/projects/borrow-against-salary/hardhat.config.js +++ /dev/null @@ -1,38 +0,0 @@ -require("@nomiclabs/hardhat-waffle") -require("@nomiclabs/hardhat-ethers") -require("dotenv").config() - -task("accounts", "Prints the list of accounts", async (_, hre) => { - const accounts = await hre.ethers.getSigners() - - for (const account of accounts) { - console.log(account.address) - } -}) - -module.exports = { - solidity: "0.8.14", - settings: { - optimizer: { - enabled: true, - runs: 1000 - } - }, - // UNCOMMENT WHEN RUNNING SCRIPTS - networks: { - hardhat: { - blockGasLimit: 100000000 - } - // goerli: { - // url: `${process.env.GOERLI_URL}`, - // accounts: [ - // `${process.env.BORROWER_PRIVATE_KEY}`, - // `${process.env.EMPLOYER_PRIVATE_KEY}`, - // `${process.env.LENDER_PRIVATE_KEY}` - // ] - // } - }, - namedAccounts: { - deployer: 0 - } -} diff --git a/projects/borrow-against-salary/package.json b/projects/borrow-against-salary/package.json deleted file mode 100644 index 72090b4..0000000 --- a/projects/borrow-against-salary/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "superfluid-isa", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "npx hardhat test --network hardhat", - "build": "npx hardhat compile" - }, - "keywords": [], - "author": "", - "license": "ISC", - "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.5", - "@nomiclabs/hardhat-waffle": "^2.0.3", - "@openzeppelin/test-helpers": "^0.5.15", - "chai": "^4.3.6", - "ethereum-waffle": "^3.4.0", - "ethers": "^5.6.0", - "hardhat": "^2.9.1" - }, - "dependencies": { - "@openzeppelin/contracts": "^4.5.0", - "@superfluid-finance/ethereum-contracts": "^1.5.0", - "@superfluid-finance/sdk-core": "^0.4.4", - "dotenv": "^16.0.0", - "graphql": "^16.3.0" - } -} diff --git a/projects/borrow-against-salary/scripts/createEmployerFlow.js b/projects/borrow-against-salary/scripts/createEmployerFlow.js deleted file mode 100644 index 991a2ef..0000000 --- a/projects/borrow-against-salary/scripts/createEmployerFlow.js +++ /dev/null @@ -1,45 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -require("dotenv").config() -//here is where you'll put your loan address that you'll be interacting with -const loanAddress = "" - -async function main() { - //note - make sure that the proper URL is added - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const employer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") - - const employerFlowOperation = daix.createFlow({ - receiver: loanAddress, - flowRate: "3858024691358024", //10k per month - }) - - console.log("running create flow script...") - - await employerFlowOperation.exec(employer).then(tx => { - console.log("Your tx succeeded!") - console.log(tx) - }) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/deleteEmployerFlow.js b/projects/borrow-against-salary/scripts/deleteEmployerFlow.js deleted file mode 100644 index cd65f49..0000000 --- a/projects/borrow-against-salary/scripts/deleteEmployerFlow.js +++ /dev/null @@ -1,45 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -require("dotenv").config() -//here is where you'll put your loan address that you'll be interacting with -const loanAddress = "" - -async function main() { - //note - make sure that the proper URL is added - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const employer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") - - const employerFlowOperation = daix.deleteFlow({ - sender: employer.address, - receiver: loanAddress, - }) - - console.log("running delete flow script...") - - await employerFlowOperation.exec(employer).then(tx => { - console.log("Your tx succeeded!") - console.log(tx) - }) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/deployFactory.js b/projects/borrow-against-salary/scripts/deployFactory.js deleted file mode 100644 index 3cc4147..0000000 --- a/projects/borrow-against-salary/scripts/deployFactory.js +++ /dev/null @@ -1,43 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -require("dotenv").config() - -async function main() { - //NOTE: this is set as the goerli url, but can be changed to reflect your RPC URL and network of choice - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const deployer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, // deployer here doesn't matter much - provider: customHttpProvider - }) - - //NOTE - this is DAIx on goerli - you can change this token to suit your network and desired token address - const daix = await sf.loadSuperToken("fDAIx") - - console.log("running deploy script...") - // We get the contract to deploy - const LoanFactory = await hre.ethers.getContractFactory("LoanFactory") - const loanFactory = await LoanFactory.connect(deployer).deploy() - - await loanFactory.deployed() - - //NOTE: you will need this address to run other scripts, so we recommend getting it from the console - console.log("LoanFactory.sol deployed to:", loanFactory.address) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/deployLoan.js b/projects/borrow-against-salary/scripts/deployLoan.js deleted file mode 100644 index 273cd8b..0000000 --- a/projects/borrow-against-salary/scripts/deployLoan.js +++ /dev/null @@ -1,67 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -const LoanFactoryABI = - require("../artifacts/contracts/LoanFactory.sol/LoanFactory.json").abi -require("dotenv").config() - -//place deployed address of the loan factory here... -const LoanFactoryAddress = "0x7b5655c12Fc4fceCCD8C3e144c413a0Ae5dc8DbA" - -//NOTE: this is set as the goerli url, but can be changed to reflect your RPC URL and network of choice -const url = process.env.GOERLI_URL - -const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - -async function main() { - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const borrower = sf.createSigner({ - privateKey: process.env.BORROWER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const employer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") //get fDAIx on goerli - - const loanFactory = new ethers.Contract( - LoanFactoryAddress, - LoanFactoryABI, - customHttpProvider - ) - - await loanFactory - .connect(borrower) - .createNewLoan( - ethers.utils.parseEther("1000"), //borrow amount = 1000 dai - 8, // 8% interest rate - 24, //24 months payback period - employer.address, //address of employer who will be effectively whitelisted in this case - borrower.address, // address of borrower - daix.address, //daix address - this is the token we'll be using: borrowing in and paying back - sf.settings.config.hostAddress //address of host - ) - .then(tx => { - console.log( - "deployment successful! here is your tx hash: ", - tx.hash - ) - }) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/getLoanById.js b/projects/borrow-against-salary/scripts/getLoanById.js deleted file mode 100644 index 71c7fb3..0000000 --- a/projects/borrow-against-salary/scripts/getLoanById.js +++ /dev/null @@ -1,41 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -const LoanFactoryABI = - require("../artifacts/contracts/LoanFactory.sol/LoanFactory.json").abi -require("dotenv").config() - -//place deployed address of the loan factory here... -const LoanFactoryAddress = "" - -//place the ID of your loan here. Note that loanIds start at 1 -const LoanId = 1 -//NOTE: this is set as the goerli url, but can be changed to reflect your RPC URL and network of choice -const url = process.env.GOERLI_URL - -const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - -async function main() { - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const loanFactory = new ethers.Contract( - LoanFactoryAddress, - LoanFactoryABI, - customHttpProvider - ) - - const loanAddress = await loanFactory.getLoanAddressByID(LoanId) - - console.log(`The address of loan ${LoanId} is ${loanAddress}`) -} - -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/initialFunding.js b/projects/borrow-against-salary/scripts/initialFunding.js deleted file mode 100644 index 9353b56..0000000 --- a/projects/borrow-against-salary/scripts/initialFunding.js +++ /dev/null @@ -1,51 +0,0 @@ -//most recent loan address -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -require("dotenv").config() - -const loanAddress = "" //NOTE: must change to reflect actual loan address - -//NOTE - this should be run first to ensure that the contract has a small token balance - -async function main() { - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const employer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const borrower = sf.createSigner({ - privateKey: process.env.BORROWER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") - - const transferAmount = ethers.utils.parseEther("100") - - const borrowerTransferOperation = daix.transfer({ - receiver: loanAddress, - amount: transferAmount // 100 dai - }) - - console.log("running transfer operation...") - - await borrowerTransferOperation.exec(borrower).then(console.log) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/lend.js b/projects/borrow-against-salary/scripts/lend.js deleted file mode 100644 index 71d95ee..0000000 --- a/projects/borrow-against-salary/scripts/lend.js +++ /dev/null @@ -1,49 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -const LoanContract = require("../artifacts/contracts/EmploymentLoan.sol/EmploymentLoan.json") -const { network } = require("hardhat") -const LoanContractABI = LoanContract.abi -require("dotenv").config() - -//NOTE -//lender should call lend on the above contract using sdk - -async function main() { - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - //most recent loan address - const loanAddress = "" //NOTE - must be updated to reflect actual loan address - - const lender = sf.createSigner({ - privateKey: process.env.LENDER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const employmentLoan = new ethers.Contract( - loanAddress, - LoanContractABI, - lender - ) - - await employmentLoan - .connect(lender) - .lend() - .then(tx => { - console.log(tx) - }) -} - -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/lenderApproval.js b/projects/borrow-against-salary/scripts/lenderApproval.js deleted file mode 100644 index 1d5447e..0000000 --- a/projects/borrow-against-salary/scripts/lenderApproval.js +++ /dev/null @@ -1,58 +0,0 @@ -//most recent loan address -const loanAddress = "" //NOTE - update w actual loan address -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -const LoanContract = require("../artifacts/contracts/EmploymentLoan.sol/EmploymentLoan.json") -const LoanContractABI = LoanContract.abi -require("dotenv").config() - -//NOTE -//lender should call lend on the above contract using sdk - -async function main() { - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const lender = sf.createSigner({ - privateKey: process.env.LENDER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") - - const employmentLoan = new ethers.Contract( - loanAddress, - LoanContractABI, - lender - ) - - const borrowAmount = await employmentLoan.borrowAmount() - - const lenderBalance = await daix.balanceOf({ - account: lender.address, - providerOrSigner: lender - }) - - const lenderApprovalOperation = daix.approve({ - receiver: employmentLoan.address, - amount: borrowAmount - }) - - await lenderApprovalOperation.exec(lender).then(tx => { - console.log(tx) - }) -} - -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/scripts/updateEmployerFlow.js b/projects/borrow-against-salary/scripts/updateEmployerFlow.js deleted file mode 100644 index d51a49d..0000000 --- a/projects/borrow-against-salary/scripts/updateEmployerFlow.js +++ /dev/null @@ -1,46 +0,0 @@ -const ethers = require("ethers") -const { Framework } = require("@superfluid-finance/sdk-core") -require("dotenv").config() -//here is where you'll put your loan address that you'll be interacting with -const loanAddress = "" - -async function main() { - //note - make sure that the proper URL is added - const url = `${process.env.GOERLI_URL}` - const customHttpProvider = new ethers.providers.JsonRpcProvider(url) - - const network = await customHttpProvider.getNetwork() - const sf = await Framework.create({ - chainId: network.chainId, - provider: customHttpProvider - }) - - const employer = sf.createSigner({ - privateKey: process.env.EMPLOYER_PRIVATE_KEY, - provider: customHttpProvider - }) - - const daix = await sf.loadSuperToken("fDAIx") - - const employerFlowOperation = sf.cfaV1.updateFlow({ - receiver: loanAddress, - flowRate: "2858024691358024", - superToken: daix.address - }) - - console.log("running update flow script...") - - await employerFlowOperation.exec(employer).then(tx => { - console.log("Your tx succeeded!") - console.log(tx) - }) -} - -// We recommend this pattern to be able to use async/await everywhere -// and properly handle errors. -main() - .then(() => process.exit(0)) - .catch(error => { - console.error(error) - process.exit(1) - }) diff --git a/projects/borrow-against-salary/test/EmploymentLoan.test.js b/projects/borrow-against-salary/test/EmploymentLoan.test.js deleted file mode 100644 index a3b996a..0000000 --- a/projects/borrow-against-salary/test/EmploymentLoan.test.js +++ /dev/null @@ -1,788 +0,0 @@ -const { Framework } = require("@superfluid-finance/sdk-core") -const { ethers , network} = require("hardhat") -const { assert } = require("chai") -const LoanArtifact = require("../artifacts/contracts/EmploymentLoan.sol/EmploymentLoan.json") -const { deployTestFramework } = require("@superfluid-finance/ethereum-contracts/dev-scripts/deploy-test-framework"); -const TestToken = require("@superfluid-finance/ethereum-contracts/build/contracts/TestToken.json") - - -let contractsFramework; -let sfDeployer; -let sf -let dai -let daix -let admin -let borrower -let lender -let employer -let loanFactory -let employmentLoan - -const alotOfEth = ethers.utils.parseEther("100000") - -before(async function () { - //get accounts from hardhat - [admin, borrower, lender, employer] = await ethers.getSigners() - - sfDeployer = await deployTestFramework(); - // deploy the framework locally - contractsFramework = await sfDeployer.frameworkDeployer.getFramework(); - - // initialize framework - sf = await Framework.create({ - chainId: 31337, - provider: admin.provider, - resolverAddress: contractsFramework.resolver, // (empty) - protocolReleaseVersion: "test" - }); - - // DEPLOYING DAI and DAI wrapper super token (which will be our `spreaderToken`) - tokenDeployment = await sfDeployer.frameworkDeployer.deployWrapperSuperToken( - "Fake DAI Token", - "fDAI", - 18, - ethers.utils.parseEther("100000000000000000000000").toString() - ); - - daix = await sf.loadSuperToken("fDAIx"); - dai = new ethers.Contract(daix.underlyingToken.address, TestToken.abi, admin); - - const LoanFactory = await ethers.getContractFactory("LoanFactory", admin) - loanFactory = await LoanFactory.deploy() - - await loanFactory.deployed() - - let borrowAmount = ethers.utils.parseEther("1000") - let interest = 10 - let paybackMonths = 12 - - await loanFactory.createNewLoan( - borrowAmount, //borrowing 1000 fDAI tokens - interest, // 10% annual interest - paybackMonths, //in months - employer.address, //address of employer - borrower.address, //address of borrower - daix.address, - sf.settings.config.hostAddress - ) - - let loanAddress = await loanFactory.idToLoan(1) - - employmentLoan = new ethers.Contract(loanAddress, LoanArtifact.abi, admin) -}) - -beforeEach(async function () { - await dai.mint(admin.address, alotOfEth) - - await dai.mint(employer.address, alotOfEth); - await dai.mint(employer.address, alotOfEth); - - await dai.mint(lender.address, alotOfEth) - - await dai.approve(daix.address, alotOfEth) - - await dai.connect(employer).approve(daix.address, alotOfEth) - - await dai.connect(lender).approve(daix.address, alotOfEth) - - await daix.upgrade(alotOfEth) - - await daix.upgrade({amount: alotOfEth}).exec(employer); - - await daix.upgrade({amount: alotOfEth}).exec(lender); - - await daix.transfer({receiver: employmentLoan.address, amount: alotOfEth}); -}) - -describe("employment loan deployment", async function () { - it("0 deploys correctly", async function () { - let borrowAmount = ethers.utils.parseEther("1000") - let interest = 10 - let paybackMonths = 12 - - let actualBorrowAmount = await employmentLoan.borrowAmount() - let actualInterest = await employmentLoan.interestRate() - let actualPaybackMonths = await employmentLoan.paybackMonths() - let acutalEmployerAddress = await employmentLoan.employer() - let actualBorrower = await employmentLoan.borrower() - let actualBorrowToken = await employmentLoan.borrowToken() - - assert.equal( - borrowAmount, - actualBorrowAmount.toString(), - "borrow amount not equal to intended amount" - ) - - assert.equal( - interest, - actualInterest, - "interest rate not equal to intended rate" - ) - - assert.equal( - paybackMonths, - actualPaybackMonths, - "payback months not equal to intended months" - ) - - assert.equal( - employer.address, - acutalEmployerAddress, - "wrong employer address" - ) - - assert.equal(borrower.address, actualBorrower, "wrong borrower address") - - assert.equal(daix.address, actualBorrowToken, "wrong borrow token") - }) -}) - -describe("Loan is initialized properly", async function () { - it("1 First flow into contract works correctly", async function () { - let employerFlowOperation = daix.createFlow({ - receiver: employmentLoan.address, - flowRate: "3215019290123456" // ~100k per year in usd - }) - - await employerFlowOperation.exec(employer) - - let employerNetFlowRate = await daix.getNetFlow({ - account: employer.address, - providerOrSigner: employer - }) - - let borrowerNetFlowRate = await daix.getNetFlow({ - account: borrower.address, - providerOrSigner: employer - }) - - let contractNetFlowRate = await daix.getNetFlow({ - account: employmentLoan.address, - providerOrSigner: employer - }) - - assert.equal(employerNetFlowRate, -3215019290123456) - - assert.equal(borrowerNetFlowRate, 3215019290123456) - - assert.equal(contractNetFlowRate, 0) - }) - - it("2 - Flow Reduction works correctly", async function () { - //testing reduction in flow - - const reduceFlowOperation = daix.updateFlow({ - receiver: employmentLoan.address, - flowRate: "1000000" - }) - - await reduceFlowOperation.exec(employer) - - const newEmployerFlowRate = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: employer - }) - - const newBorrowerFlowRate = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const newContractFlowRate = await daix.getNetFlow({ - account: employmentLoan.address, - providerOrSigner: employer - }) - - assert.equal( - newEmployerFlowRate.flowRate, - "1000000", - "wrong employer flow rate" - ) - - assert.equal( - newBorrowerFlowRate.flowRate, - "1000000", - "wrong borrower flow rate" - ) - - assert.equal(newContractFlowRate, 0, "contract is not balanced") - }) - - it("3 - should show that a loan is not closed (anyone can become a lender)", async function () { - //before the lend function is called, both isClosed and loanOpen are false - - const loanOpen = await employmentLoan.loanOpen() - assert.equal(loanOpen, false); - - const isClosed = await employmentLoan.isClosed() - assert.equal(isClosed, false); - }) - - it("4 Lend Function works correctly", async function () { - //should reduce flow rate, test to ensure failure, then test update flow rate - //try calling lend - should revert - - const borrowAmount = await employmentLoan.borrowAmount() - - const daixApproval = daix.approve({receiver: employmentLoan.address, amount: borrowAmount}); - await daixApproval.exec(lender); - - const employerUpdateFlowOperation = daix.updateFlow({ - receiver: employmentLoan.address, - flowRate: "3215019290123456" - }) - - await employerUpdateFlowOperation.exec(employer) - - let borrowerBalBefore = await daix.balanceOf({account: borrower.address, providerOrSigner: admin}); - - let lenderBalBefore = await daix.balanceOf({account: lender.address, providerOrSigner: admin}); - - let borrowerFlowRateBefore = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - - //SENDING A SMALL AMOUNT OF FUNDS INTO CONTRACT FOR BUFFER - await daix.transfer({receiver: employmentLoan.address, amount: ethers.utils.parseEther("100")}).exec(employer); - - await employmentLoan.connect(lender).lend() - - let lenderBalAfter = await daix.balanceOf({account: lender.address, providerOrSigner: admin}); - - let borrowerBalAfter = await daix.balanceOf({account: borrower.address, providerOrSigner: admin}); - - let borrowerFlowRateAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - let lenderFlowRateAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - - let employerFlow = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: lender - }) - - let expectedLender = await employmentLoan.lender() - let loanStartedTime = await employmentLoan.loanStartTime() - - let expectedFlowRate = await employmentLoan.getPaymentFlowRate() - - assert.isAtLeast( - Number(borrowerBalBefore + borrowAmount), - Number(borrowerBalAfter), - "borrower bal did not increase enough" - ) - - assert.isAtMost( - lenderBalBefore - borrowAmount, - Number(lenderBalAfter), - "lender should have less money" - ) - - assert.equal( - Number(borrowerFlowRateAfter.flowRate), - Number( - Number(employerFlow.flowRate) - - Number(lenderFlowRateAfter.flowRate) - ), - "borrower flow rate incorrect" - //borrower flow rate should decrease by paymentFlowrate amount after lend is called - ) - - assert.equal( - //lender flow rate should increase by proper amount when lend is called - Number(lenderFlowRateAfter.flowRate), - Number(borrowerFlowRateBefore.flowRate) - - (Number(borrowerFlowRateBefore.flowRate) - expectedFlowRate), - "lender flowRate incorrect" - ) - - assert.equal( - Number(lender.address), - Number(expectedLender), - "lender is not correct" - ) - - assert.notEqual( - loanStartedTime, - 0, - "loan has not been started properly" - ) - }) - - it("5 - flow is reduced", async function () { - const updateFlowOp = await daix.updateFlow({ - receiver: employmentLoan.address, - flowRate: "10000" - }) - - await updateFlowOp.exec(employer) - - const borrowFlowToLender = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const borrowerNewFlowRate = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - assert.equal( - borrowFlowToLender.flowRate, - "10000", - "lender should be getting remainder" - ) - //remaining amount should go to borrower - assert.equal( - borrowerNewFlowRate.flowRate, - 0, - "borrower new flow should be zero" - ) - }) - - it("6 - should allow a loan to become solvent again after a flow is reduced", async function () { - let employerFlowOperation = daix.updateFlow({ - receiver: employmentLoan.address, - flowRate: "3215019290123456" // ~100k per year in usd - }) - - await employerFlowOperation.exec(employer) - - const employerFlowRate = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: lender - }) - - const borrowTokenFlowToLenderAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - //should be total inflow to contract from employer - flow to lender - - const borrowTokenFlowToBorrowerAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: lender - }) - - //should be total inflow to contract from employer - flow to lender - - assert.equal( - Number(borrowTokenFlowToBorrowerAfter.flowRate), - Number( - Number(employerFlowRate.flowRate) - - Number(borrowTokenFlowToLenderAfter.flowRate) - ), - "borrower flow rate incorrect" - //borrower flow rate should decrease by paymentFlowrate amount after lend is called - ) - - assert.equal( - //lender flow rate should increase by proper amount when lend is called - Number(borrowTokenFlowToLenderAfter.flowRate), - Number(employerFlowRate.flowRate) - - Number(borrowTokenFlowToBorrowerAfter.flowRate), - "lender flowRate incorrect" - ) - }) - - it("7 - flow is deleted", async function () { - //delete flow - - const deleteFlowOp = await daix.deleteFlow({ - sender: employer.address, - receiver: employmentLoan.address - }) - - await deleteFlowOp.exec(employer) - - const newEmployerFlowRate = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: employer - }) - - const borrowFlowToLender = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const borrowerNewFlowRate = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - assert.equal( - newEmployerFlowRate.flowRate, - 0, - "employer to contract flow rate should be 0" - ) - - assert.equal( - borrowFlowToLender.flowRate, - 0, - "lender should no longer receive daix" - ) - - //remaining amount should go to borrower - assert.equal( - borrowerNewFlowRate.flowRate, - "0", - "borrower new flow should be zero" - ) - }) - - it("8 - should allow loan to become solvent again after deletion ", async function () { - //re start flow - - let employerFlowOperation = daix.createFlow({ - receiver: employmentLoan.address, - flowRate: "3215019290123456" // ~100k per year in usd - }) - - await employerFlowOperation.exec(employer) - - const employerFlowRate = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: lender - }) - - const borrowTokenFlowToLenderAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - //should be total inflow to contract from employer - flow to lender - - const borrowTokenFlowToBorrowerAfter = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: lender - }) - - //should be total inflow to contract from employer - flow to lender - assert.equal( - Number(borrowTokenFlowToBorrowerAfter.flowRate), - Number( - Number(employerFlowRate.flowRate) - - Number(borrowTokenFlowToLenderAfter.flowRate) - ), - "borrower flow rate incorrect" - //borrower flow rate should decrease by paymentFlowrate amount after lend is called - ) - assert.equal( - //lender flow rate should increase by proper amount when lend is called - Number(borrowTokenFlowToLenderAfter.flowRate), - Number(employerFlowRate.flowRate) - - Number(borrowTokenFlowToBorrowerAfter.flowRate), - "lender flowRate incorrect" - ) - }) - - //todo fix - looks like transfer and approve opp don't work here - it("9 closing the loan early with payment from borrower", async function () { - //borrower sends payment to pay off loan - const amountLeft = await employmentLoan - .connect(borrower) - .getTotalAmountRemaining() - const lenderBalBefore = await daix.balanceOf({account: lender.address, providerOrSigner: admin}) - - //somewhat impractical, but we'll assume that the borrower is sent money from lender (they just need the money in general to pay off loan) - await daix.transfer({receiver: borrower.address, amount: amountLeft}).exec(lender); - - await daix.approve({receiver: employmentLoan.address, amount: amountLeft}).exec(borrower); - - await employmentLoan.connect(borrower).closeOpenLoan(amountLeft) - - const lenderFlowRateAfterCompletion = await daix.getFlow({ - sender: employmentLoan.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const borrowerFlowRateAfterCompletion = await daix.getFlow({ - sender: employmentLoan.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const employerFlowRateAfterCompletion = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan.address, - providerOrSigner: employer - }) - - const loanStatus = await employmentLoan.loanOpen() - - assert.equal(loanStatus, false) - - //This is exactly why there is a need for another variable besides @loanOpen - to differentiate between loans before and after being paid off. - //It's impossible to do that with just @loanOpen. - const isClosed = await employmentLoan.isClosed() - assert.equal(isClosed, true); - - assert.equal( - lenderFlowRateAfterCompletion.flowRate, - 0, - "lender flow rate should now be zero" - ) - assert.equal( - borrowerFlowRateAfterCompletion.flowRate, - employerFlowRateAfterCompletion.flowRate, - "employer should now send 100% of flow to employee" - ) - assert.isAtLeast( - Number(Number(lenderBalBefore) + Number(amountLeft)), - Number(lenderBalBefore), - "lender should see increase in borrow token balance" - ) - }) - - it("10 closing the loan early from lender", async function () { - //other party sends payment to pay off loan - let borrowAmount = ethers.utils.parseEther("1000") - let interest = 10 - let paybackMonths = 12 - - await loanFactory.createNewLoan( - borrowAmount, //borrowing 1000 fDAI tokens - interest, // 10% annual interest - paybackMonths, //in months - employer.address, //address of employer - borrower.address, //address of borrower - daix.address, - sf.settings.config.hostAddress - ) - - let loan2Address = await loanFactory.idToLoan(2) - let employmentLoan2 = new ethers.Contract( - loan2Address, - LoanArtifact.abi, - admin - ) - - await dai.connect(borrower).mint(borrower.address, alotOfEth); - await dai.connect(borrower).approve(daix.address, alotOfEth) - await daix.upgrade({amount: alotOfEth}).exec(borrower); - - await daix.transfer({receiver: employmentLoan2.address, amount: alotOfEth}).exec(borrower); - - //create flow - - const createFlowOperation = daix.createFlow({ - receiver: employmentLoan2.address, - flowRate: "3215019290123456" - }) - - await createFlowOperation.exec(employer) - - //lend - - await daix.approve({receiver: employmentLoan2.address, amount: borrowAmount.toString()}).exec(lender); - - //SENDING A SMALL AMOUNT OF FUNDS INTO CONTRACT FOR BUFFER - // await daix.transfer({receiver: employmentLoan.address, amount: ethers.utils.parseEther("100")}).exec(employer); - - await employmentLoan2.connect(lender).lend() - - //make sure it worked - const borrowerFlow = await daix.getFlow({ - sender: employmentLoan2.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const lenderFlow = await daix.getFlow({ - sender: employmentLoan2.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const employerFlow = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan2.address, - providerOrSigner: employer - }) - - let pass6Months = 86400 * (365 / 2) - await network.provider.send("evm_increaseTime", [pass6Months]) - await network.provider.send("evm_mine") - - //close loan before it ends - await employmentLoan2.connect(lender).closeOpenLoan(0) - - const borrowerFlowAfter = await daix.getFlow({ - sender: employmentLoan2.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const lenderFlowAfter = await daix.getFlow({ - sender: employmentLoan2.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const loanStatus = await employmentLoan2.loanOpen() - assert.equal(loanStatus, false) - - const isClosed = await employmentLoan2.isClosed() - assert.equal(isClosed, true); - - assert.isBelow( - Number(borrowerFlow.flowRate), - Number(employerFlow.flowRate), - "borrower flow rate should be less than total amount sent into loan by employer prior to the closing of the loan" - ) - assert.isAbove( - Number(lenderFlow.flowRate), - 0, - "lender should have positive flow rate prior to loan ending" - ) - assert.equal( - borrowerFlowAfter.flowRate, - employerFlow.flowRate, - "borrower flow after loan ends should be equal to full value of employer flow" - ) - assert.equal( - lenderFlowAfter.flowRate, - 0, - "lender flow rate should be zero after" - ) - }) - - it("11 borrower closing the loan once completed", async function () { - //borrower closes loan once complete - let borrowAmount = ethers.utils.parseEther("1000") - let interest = 10 - let paybackMonths = 12 - - await loanFactory.createNewLoan( - borrowAmount, //borrowing 1000 fDAI tokens - interest, // 10% annual interest - paybackMonths, //in months - employer.address, //address of employer - borrower.address, //address of borrower - daix.address, - sf.settings.config.hostAddress - ) - - let loan3Address = await loanFactory.idToLoan(3) - - let employmentLoan3 = new ethers.Contract( - loan3Address, - LoanArtifact.abi, - admin - ) - - - await dai.connect(borrower).mint(borrower.address, alotOfEth) - await dai.connect(borrower).approve(daix.address, alotOfEth); - await daix.upgrade({amount: alotOfEth}).exec(borrower); - await daix.transfer({receiver: employmentLoan3.address, amount: alotOfEth}).exec(borrower); - - //create flow - - const createLoan3FlowOperation = daix.createFlow({ - receiver: employmentLoan3.address, - flowRate: "3215019290123456" - }) - - await createLoan3FlowOperation.exec(employer) - - //lend - - await daix.approve({receiver: employmentLoan3.address, amount: borrowAmount.toString()}).exec(lender); - - //SENDING A SMALL AMOUNT OF FUNDS INTO CONTRACT FOR BUFFER - await daix.transfer({receiver: employmentLoan.address, amount: ethers.utils.parseEther("100")}).exec(employer); - - await employmentLoan3.connect(lender).lend() - - //make sure it worked - const borrowerFlow = await daix.getFlow({ - sender: employmentLoan3.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const lenderFlow = await daix.getFlow({ - sender: employmentLoan3.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const employerFlow = await daix.getFlow({ - sender: employer.address, - receiver: employmentLoan3.address, - providerOrSigner: employer - }) - - //we will close loan 1 hour after the loan expires - let passLoanDuration = 86400 * (365 / 12) * paybackMonths + 3600 - await network.provider.send("evm_increaseTime", [passLoanDuration]) - await network.provider.send("evm_mine") - - //close loan before it ends - await employmentLoan3.connect(borrower).closeCompletedLoan() - - const borrowerFlowAfter = await daix.getFlow({ - sender: employmentLoan3.address, - receiver: borrower.address, - providerOrSigner: borrower - }) - - const lenderFlowAfter = await daix.getFlow({ - sender: employmentLoan3.address, - receiver: lender.address, - providerOrSigner: lender - }) - - const loanStatus = await employmentLoan3.loanOpen(); - assert.equal(loanStatus, false); - - const isClosed = await employmentLoan3.isClosed() - assert.equal(isClosed, true); - - assert.isBelow( - Number(borrowerFlow.flowRate), - Number(employerFlow.flowRate), - "borrower flow rate should be less than total amount sent into loan by employer prior to the closing of the loan" - ); - assert.isAbove( - Number(lenderFlow.flowRate), - 0, - "lender should have positive flow rate prior to loan ending" - );; - assert.equal( - borrowerFlowAfter.flowRate, - employerFlow.flowRate, - "borrower flow after loan ends should be equal to full value of employer flow" - ); - assert.equal( - lenderFlowAfter.flowRate, - 0, - "lender flow rate should be zero after" - ); - }); -}); \ No newline at end of file diff --git a/projects/flow-splitter/.gitignore b/projects/flow-splitter/.gitignore deleted file mode 100644 index 00dad77..0000000 --- a/projects/flow-splitter/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -node_modules -.env -coverage -coverage.json -typechain -typechain-types - -# Hardhat files -cache -artifacts - diff --git a/projects/flow-splitter/README.md b/projects/flow-splitter/README.md deleted file mode 100644 index 5cb8cf7..0000000 --- a/projects/flow-splitter/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Flow Splitter Super App - -This project demonstrates a Superfluid Super App smart contract that routes incoming streams to two receiver accounts on the basis of a set proportions. - -

-
-diagram -
-

- -It also does a great job of demonstrating the usage of the SuperAppBaseCFA. \ No newline at end of file diff --git a/projects/flow-splitter/contracts/FlowSplitter.sol b/projects/flow-splitter/contracts/FlowSplitter.sol deleted file mode 100644 index c21636f..0000000 --- a/projects/flow-splitter/contracts/FlowSplitter.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity 0.8.18; - -// Uncomment this line to use console.log -// import "hardhat/console.sol"; - -import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; -import {SuperAppBaseFlow} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBaseFlow.sol"; -import { - ISuperfluid, - ISuperToken -} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; - -/// @title FlowSplitter -/// @author Superfluid | Modified by @0xdavinchee -/// @dev A negative sideReceiverPortion portion is not allowed -/// A portion > 1000 is fine though because the protocol will -/// revert when trying to create a flow with a negative flow rate -/// A flowRate which is less than 1000 will be rounded down to 0 and will revert -/// Also an inflow which does not contain a whole number will be rounded down, -/// this will also lead to a revert. -contract FlowSplitter is SuperAppBaseFlow { - using SuperTokenV1Library for ISuperToken; - - /// @dev Account that ought to be routed the majority of the inflows - address public immutable MAIN_RECEIVER; - - /// @dev Account that ought to be routed the minority of the inflows - address public immutable SIDE_RECEIVER; - - /// @dev Account that deployed the contract - address public immutable CREATOR; - - /// @dev Super Token that the FlowSplitter will accept streams of - ISuperToken public immutable ACCEPTED_SUPER_TOKEN; - - /// @dev number out of 1000 representing portion of inflows to be redirected to SIDE_RECEIVER - /// Ex: 300 would represent 30% - int96 public sideReceiverPortion; - - error INVALID_PORTION(); - error SAME_RECEIVERS_NOT_ALLOWED(); - error NO_SELF_FLOW(); - error NOT_CREATOR(); - - /// @dev emitted when the split of the outflow to MAIN_RECEIVER and SIDE_RECEIVER is updated - event SplitUpdated(int96 mainReceiverPortion, int96 newSideReceiverPortion); - - constructor( - ISuperfluid host_, - ISuperToken acceptedSuperToken_, - address creator_, - address mainReceiver_, - address sideReceiver_, - int96 sideReceiverPortion_ - ) SuperAppBaseFlow(host_, true, true, true) { - if (sideReceiverPortion_ <= 0 || sideReceiverPortion_ == 1000) revert INVALID_PORTION(); - if (mainReceiver_ == sideReceiver_) revert SAME_RECEIVERS_NOT_ALLOWED(); - if (mainReceiver_ == address(this) || sideReceiver_ == address(this)) revert NO_SELF_FLOW(); - - ACCEPTED_SUPER_TOKEN = acceptedSuperToken_; - CREATOR = creator_; - MAIN_RECEIVER = mainReceiver_; - SIDE_RECEIVER = sideReceiver_; - sideReceiverPortion = sideReceiverPortion_; - } - - /// @dev checks that only the acceptedToken is used when sending streams into this contract - /// @param superToken_ the token being streamed into the contract - function isAcceptedSuperToken(ISuperToken superToken_) public view override returns (bool) { - return superToken_ == ACCEPTED_SUPER_TOKEN; - } - - /// @notice Returns the outflow rates to main and side receiver given flowRate_ and an arbitrary - /// sideReceiverPortion_ - /// @dev If either returns 0, it will revert when trying to create a flow - /// because the protocol does not allow creating flows with a flow rate of 0 - /// Also, if the sum of the two outflows is not equal to the inflow, it means the app will - /// receive a residual flow. - /// @param flowRate_ the inflow rate - /// @param sideReceiverPortion_ the portion of the inflow to be redirected to SIDE_RECEIVER - /// @return mainFlowRate the outflow rate to MAIN_RECEIVER - /// @return sideFlowRate the outflow rate to SIDE_RECEIVER - /// @return residualFlowRate the residual flow rate - function getMainAndSideReceiverFlowRates(int96 flowRate_, int96 sideReceiverPortion_) - external - pure - returns (int96 mainFlowRate, int96 sideFlowRate, int96 residualFlowRate) - { - mainFlowRate = (flowRate_ * (1000 - sideReceiverPortion_)) / 1000; - sideFlowRate = (flowRate_ * sideReceiverPortion_) / 1000; - residualFlowRate = flowRate_ - (mainFlowRate + sideFlowRate); - } - - /// @notice Updates the split of the outflow to MAIN_RECEIVER and SIDE_RECEIVER - /// @dev Only the creator should be able to call update split. - /// @param newSideReceiverPortion_ the new portion of inflows to be redirected to SIDE_RECEIVER - function updateSplit(int96 newSideReceiverPortion_) external { - if (newSideReceiverPortion_ <= 0 || newSideReceiverPortion_ >= 1000) revert INVALID_PORTION(); - if (msg.sender != CREATOR) revert NOT_CREATOR(); - - sideReceiverPortion = newSideReceiverPortion_; - - // get current outflow rate - int96 totalOutflowRate = ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), MAIN_RECEIVER) - + ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), SIDE_RECEIVER); - - int96 mainReceiverPortion = 1000 - newSideReceiverPortion_; - - // update outflows - // @note there is a peculiar bug here where you can't change the outflow in any way - ACCEPTED_SUPER_TOKEN.updateFlow(MAIN_RECEIVER, (totalOutflowRate * mainReceiverPortion) / 1000); - ACCEPTED_SUPER_TOKEN.updateFlow(SIDE_RECEIVER, (totalOutflowRate * newSideReceiverPortion_) / 1000); - - emit SplitUpdated(mainReceiverPortion, newSideReceiverPortion_); - } - - // --------------------------------------------------------------------------------------------- - // CALLBACK LOGIC - - function onFlowCreated(ISuperToken superToken_, address sender_, bytes calldata ctx_) - internal - override - returns (bytes memory newCtx) - { - newCtx = ctx_; - - // get inflow rate from sender_ - int96 inflowRate = superToken_.getFlowRate(sender_, address(this)); - - // if there's no outflow already, create outflows - if (superToken_.getFlowRate(address(this), MAIN_RECEIVER) == 0) { - newCtx = - superToken_.createFlowWithCtx(MAIN_RECEIVER, (inflowRate * (1000 - sideReceiverPortion)) / 1000, newCtx); - - newCtx = superToken_.createFlowWithCtx(SIDE_RECEIVER, (inflowRate * sideReceiverPortion) / 1000, newCtx); - } - // otherwise, there's already outflows which should be increased - else { - newCtx = superToken_.updateFlowWithCtx( - MAIN_RECEIVER, - ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), MAIN_RECEIVER) - + (inflowRate * (1000 - sideReceiverPortion)) / 1000, - newCtx - ); - - newCtx = superToken_.updateFlowWithCtx( - SIDE_RECEIVER, - ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), SIDE_RECEIVER) - + (inflowRate * sideReceiverPortion) / 1000, - newCtx - ); - } - } - - function onFlowUpdated( - ISuperToken superToken_, - address sender_, - int96 previousFlowRate_, - uint256, /*lastUpdated*/ - bytes calldata ctx_ - ) internal override returns (bytes memory newCtx) { - newCtx = ctx_; - - // get inflow rate change from sender_ - int96 inflowChange = superToken_.getFlowRate(sender_, address(this)) - previousFlowRate_; - - // update outflows - newCtx = superToken_.updateFlowWithCtx( - MAIN_RECEIVER, - ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), MAIN_RECEIVER) - + (inflowChange * (1000 - sideReceiverPortion)) / 1000, - newCtx - ); - - newCtx = superToken_.updateFlowWithCtx( - SIDE_RECEIVER, - ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), SIDE_RECEIVER) + (inflowChange * sideReceiverPortion) / 1000, - newCtx - ); - } - - function onFlowDeleted( - ISuperToken superToken_, - address, /*sender_*/ - address receiver_, - int96 previousFlowRate_, - uint256, /*lastUpdated*/ - bytes calldata ctx_ - ) internal override returns (bytes memory newCtx) { - newCtx = ctx_; - - // remaining inflow is equal to total outflow less the inflow that just got deleted - int96 remainingInflow = ( - ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), MAIN_RECEIVER) - + ACCEPTED_SUPER_TOKEN.getFlowRate(address(this), SIDE_RECEIVER) - ) - previousFlowRate_; - - // handle "rogue recipients" with sticky stream - see readme - if (receiver_ == MAIN_RECEIVER || receiver_ == SIDE_RECEIVER) { - newCtx = superToken_.createFlowWithCtx(receiver_, previousFlowRate_, newCtx); - } - - // if there is no more inflow, outflows should be deleted - if (remainingInflow <= 0) { - newCtx = superToken_.deleteFlowWithCtx(address(this), MAIN_RECEIVER, newCtx); - - newCtx = superToken_.deleteFlowWithCtx(address(this), SIDE_RECEIVER, newCtx); - } - // otherwise, there's still inflow left and outflows must be updated - else { - newCtx = superToken_.updateFlowWithCtx( - MAIN_RECEIVER, (remainingInflow * (1000 - sideReceiverPortion)) / 1000, newCtx - ); - - newCtx = - superToken_.updateFlowWithCtx(SIDE_RECEIVER, (remainingInflow * sideReceiverPortion) / 1000, newCtx); - } - } -} diff --git a/projects/flow-splitter/hardhat.config.js b/projects/flow-splitter/hardhat.config.js deleted file mode 100644 index d5dd5f4..0000000 --- a/projects/flow-splitter/hardhat.config.js +++ /dev/null @@ -1,6 +0,0 @@ -require("@nomicfoundation/hardhat-toolbox"); - -/** @type import('hardhat/config').HardhatUserConfig */ -module.exports = { - solidity: "0.8.18", -}; diff --git a/projects/flow-splitter/package.json b/projects/flow-splitter/package.json deleted file mode 100644 index 0f12d51..0000000 --- a/projects/flow-splitter/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "hardhat-project", - "devDependencies": { - "@nomicfoundation/hardhat-toolbox": "^2.0.2", - "hardhat": "^2.14.0" - }, - "dependencies": { - "@superfluid-finance/ethereum-contracts": "^1.6.0", - "@superfluid-finance/sdk-core": "^0.6.5" - } -} diff --git a/projects/flow-splitter/resources/diagram.png b/projects/flow-splitter/resources/diagram.png deleted file mode 100644 index 48fddd01b2b8282f38e1dc0f62de66750f2dbce3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64924 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 diff --git a/projects/flow-splitter/scripts/deploy.js b/projects/flow-splitter/scripts/deploy.js deleted file mode 100644 index 88becbb..0000000 --- a/projects/flow-splitter/scripts/deploy.js +++ /dev/null @@ -1,40 +0,0 @@ -// We require the Hardhat Runtime Environment explicitly here. This is optional -// but useful for running the script in a standalone fashion through `node