{"id":13408582,"url":"https://github.com/PierreBlg/PublicFlashLoansSC","last_synced_at":"2025-03-14T13:31:36.149Z","repository":{"id":207423223,"uuid":"719215702","full_name":"PierreBlg/PublicFlashLoansSC","owner":"PierreBlg","description":null,"archived":false,"fork":false,"pushed_at":"2023-11-15T17:59:58.000Z","size":94,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-07-31T20:31:31.469Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PierreBlg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-11-15T17:46:44.000Z","updated_at":"2024-01-08T13:40:15.000Z","dependencies_parsed_at":"2023-11-15T18:47:38.710Z","dependency_job_id":null,"html_url":"https://github.com/PierreBlg/PublicFlashLoansSC","commit_stats":null,"previous_names":["pierreblg/publicflashloanssc"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PierreBlg%2FPublicFlashLoansSC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PierreBlg%2FPublicFlashLoansSC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PierreBlg%2FPublicFlashLoansSC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PierreBlg%2FPublicFlashLoansSC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PierreBlg","download_url":"https://codeload.github.com/PierreBlg/PublicFlashLoansSC/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243584314,"owners_count":20314732,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:00:53.812Z","updated_at":"2025-03-14T13:31:35.690Z","avatar_url":"https://github.com/PierreBlg.png","language":"Rust","funding_links":[],"categories":["MultiversX community"],"sub_categories":["Smart contracts"],"readme":"\n# Description step by step\n\nWe will go through the contracts step by step, and will describe each function and what is his job in the whole workflow.\n\n\n\n## Setup\n\n- Clone the project\n\n- In FastLoanSC, pair and fees-collector folders launch :\n\n```bash\n  mxpy contract build\n```\n\n# Documentation\n\nThe **action_key** of the user is here to keep tracking of the workflow and storages in pool and flashloan contracts. It is unique per call and permit to be certain of the uniqueness of the transaction and workflow.\n\n\n## Flashloan SC\n\n```rust\n#[init]\nfn init(\n    \u0026self,\n    fees : u64,\n) {}\n```\n\nThe init function only takes fees percentage. The fees need to be the same and to be set in both pool and flashloan SC.\n\n```rust\n#[only_owner]\n#[endpoint(setupPoolsExchanges)]\nfn owner_setup_pool_addresses_pairs(\u0026self, pool_addresses : MultiValueEncoded\u003c(Exchange, SwapContractAddress\u003cSelf::Api\u003e)\u003e) {}\n```\n\nThis function is here to save the pool addresses, exchange and token associated to each address. It will avoid too long user input.\n\n```rust\n#[endpoint(borrow)]\nfn borrow(\u0026self, borrows: MultiValueEncoded\u003cBorrow\u003cSelf::Api\u003e\u003e, actions : MultiValueEncoded\u003cUserAction\u003cSelf::Api\u003e\u003e) {}\n```\n\nThe borrow function needs 2 arguments :\n\n- **borrows** which is a list of Token/Amount pairs to borrow :\n\n```rust\npub struct Borrow\u003cM: ManagedTypeApi\u003e {\n    pub token : TokenIdentifier\u003cM\u003e,\n    pub amount : BigUint\u003cM\u003e,\n}\n```\n\n- **actions** which is a list of Exchange (1, 2, 3 or 4), first_token, second_token and amount to swap from first to second :\n\n```rust\npub struct UserAction\u003cM: ManagedTypeApi\u003e {\n    pub exchange : Exchange,\n    pub first_token : TokenIdentifier\u003cM\u003e,\n    pub second_token : TokenIdentifier\u003cM\u003e,\n    pub amount_swap : BigUint\u003cM\u003e\n}\n\n```\n\nAll the borrows are made in xExchange pools and we cannot swap in the same pools we borrowed from.\n\nAt the end of the borrow function, we call *borrow_liquidity* of first pool :\n\n```rust\nself.pair_proxy(borrow_address)\n  .borrow_liquidity(\n      EsdtTokenPayment::new(borrow.token, 0, borrow.amount),\n      action_key\n  )\n  .execute_on_dest_context()\n```\n\nThe *borrow_liquidity* function check if everything is fine to borrow (reserves, authorizations, tokens asked, ...) and then save in storage a copy of borrowed token.\nThen call *deposit_loan* function in the FlashLoanSC\n\n```rust\nself.borrowed_tokens(action_key).update(|f| f.push(\n        payment.clone()\n    ));\nself.flash_loan_proxy(loan_address)\n.deposit_loan(action_key)\n.with_esdt_transfer(payment)\n.execute_on_dest_context()\n```\n\nIn *deposit_loan* function, we save all deposits per address and in a list. If we need other deposits, we launch again borrow_liquidity (it's a loop) with the new pool and token asked. When the last deposit is made, the workflow continues and executes user actions, by taking address of the actions using their exchange number,tokens and amounts from user input.\n\n```rust \nfn execute_user_actions_follow_balance (\n    \u0026self,\n    action_key : u64\n) -\u003e ManagedVec\u003cEsdtTokenPayment\u003e {}\n```\n\n**We keep tracking of the current balances** to check at the end of the workflow if token borrow balances are enough **to pay back pools**.\n\nAfter user actions, we check balances and if user is able to give back to pools borrowed amounts + additional fees :\n\n```rust \nfn check_actions_result_is_enough(\n      \u0026self,\n      action_key : u64,\n      all_balances : ManagedVec\u003cEsdtTokenPayment\u003e\n  ) -\u003e ManagedVec\u003cEsdtTokenPayment\u003e {}\n```\n\nSo if a user borrows 100 EGLD, with 1% fee, he needs to give back 101 EGLD.\n\nThen the additional amount is sent to the user, the borrowed + fees amount is sent to pools\n\nPools check again using their saved borrowed token if everything is ok, and then pools send fees to collector.\n\nAt the complete end, after pay back, stack goes back in pools, in *borrow_liquidity* function, after each deposit to flashloan, and check that the liquidity is the same as before execution.\n\nThe workflow is the following :\n\n```\n1. Borrow tokens\n2. Ask pools to deposit in deposit_loan \n3. Pool save borrows\n3. Wait for all deposits in FlSC\n4. Executes user actions and track balances\n5. Check if user can pay back amounts + fees to pools \n6. Check amount the user will receive\n7. Send back amounts + fees to pool \n8. Pools send extra fees to collector\n9. User receive his extra amount of tokens\n10. Each pool check in borrow_liquidity that liquidity is the same as before\n```\n\n\n## Running Tests\n\nTo be certain everything is working as expected, whiteboxes tests were made. \n\nTo test the smart contract, go in FastLoanSC folder and launch :\n\n```bash\n  cargo test\n```\n\n\n## Tests\n\n**try_setup** - Setup the contracts\n\n**borrow_to_not_set_exchange_pool** - Borrow an imaginary token\n\n**fastloan_deposit_from_unauthorized** - Call deposit_loan from unauthorized address\n\n**pool_borrow_incorrect_token** - FastloanSC tries to borrow incorrect tokens from pool\n\n**pool_endpoints_unauthorized** - Force calling endpoint with unauthorized address\n\n**borrow_more_than_pool_reserve** - Borrow more than pool has tokens\n\n**user_action_to_unknown_contract** - User action is directed to an unknown contract\n\n**borrow_give_back_no_fee** - Borrow 100 tokens and give them back (no fees)\n\n**borrow_two_tokens_give_back_no_fee** - Borrow 100 of two tokens and give them back (no fees)\n\n**arbitrage_between_two_differents_exchanges_pools_fees** - Do arbitrage between 2 pools of 2 differents exchanges, and test swap and borrow same pool\n\n**borrow_and_swap_other_pool_swap_back** - Swap EGLD to ASH and ASH to WEGLD using the same pool. Cannot pay fees.\n\n**pool_borrow_and_swap_same_pool** - Try to borrow and to swap in the same pool to check if price imbalanced, and then swap in another exchange to give back to pool -\u003e Price is calculated again between deposit and give back funds due to storage drop before deposit, cannot exploit borrowing","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPierreBlg%2FPublicFlashLoansSC","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FPierreBlg%2FPublicFlashLoansSC","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FPierreBlg%2FPublicFlashLoansSC/lists"}