# oak.credit > Borrow dollars against tokenized stocks and get cashback. A neobank on Robinhood Chain (chain id 4663, Arbitrum Orbit L2). Fixed-term peer-to-peer loans, no oracle, no liquidations: a lender escrows USDG at a price and term they set, a borrower locks a Robinhood stock token and takes USDG, repays by the due date or the lender claims the stock. Live on mainnet. ## Contracts (Robinhood Chain) - OakLoans: 0xe4FFED90e5EABFB12B068Da673607514F9C6f469 (https://robinhoodchain.blockscout.com/address/0xe4FFED90e5EABFB12B068Da673607514F9C6f469) deployed at block 61801347 - USDG stablecoin (Paxos Global Dollar, 6 decimals): 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 - Collateral: 194 Robinhood-issued stock tokens (18 decimals), e.g. NVDA 0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC, SPCX 0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa, AAPL 0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9, TSLA 0x322F0929c4625eD5bAd873c95208D54E1c003b2d, GOOGL 0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3, SPY 0x117cc2133c37B721F49dE2A7a74833232B3B4C0C, MSFT 0xe93237C50D904957Cf27E7B1133b510C669c2e74, AMZN 0x12f190a9F9d7D37a250758b26824B97CE941bF54 - Fee: 10% of interest on repayment to treasury 0x5fF5C224dd3040eDA15A84064A1913Df87718deA. No fee on principal or claims. Not upgradeable. Unaudited. - RPC: https://rpc.mainnet.chain.robinhood.com/rpc ## Pages - Landing: https://oak-credit.vercel.app/ - App: https://oak-credit.vercel.app/app (borrow, earn, offerbook, pay, stash) - Simulated demo, no wallet needed: https://oak-credit.vercel.app/dev - Docs (how every function works, worked example, risks, API): https://oak-credit.vercel.app/docs - Code (contract source verbatim, ABI): https://oak-credit.vercel.app/code - Tech (live stats, every address, allowlist with prices): https://oak-credit.vercel.app/tech - Terms: https://oak-credit.vercel.app/terms · Privacy: https://oak-credit.vercel.app/privacy - Full machine-readable version with source and API responses: https://oak-credit.vercel.app/llms-full.txt ## Read API (public, JSON, no key) - GET /api/oak/offers (open offers, cheapest APR first; ?ticker=TSLA) - GET /api/oak/loans?user=0x… - GET /api/oak/activity?user=0x… - GET /api/oak/prices (USD per token from DexScreener, display only) - GET /api/oak/stats ## Contract functions createOffer(collateral, amount, minTake, term, aprBps, collateralPerUnit, expiry) · cancelOffer(offerId) · take(offerId, principal) · repay(loanId) · claim(loanId) · quoteCollateral(offerId, principal) · quoteInterest(offerId, principal) · getOffers(from, count) · getLoans(from, count) · offersOf(address) · loansOf(address) ## Contact X: https://x.com/oakcredit · Telegram: https://t.me/oak_credit ## How a loan works, precisely - Offer: lender escrows amount USDG. collateralPerUnit = stock wei required per 1 USDG (the lender's price and LTV in one number; the app computes 1e18 / (price × LTV)). term in seconds (1 to 365 days). aprBps simple annual interest. expiry unix timestamp. Borrowers may take part of an offer, never below minTake and never leaving a remainder below it. - Take: collateral = ceil(principal × collateralPerUnit / 1e6) pulled into escrow; principal USDG sent to borrower; interest = principal × aprBps × term / (365 days × 10000) fixed at that moment; dueAt = now + term. - Repay: any time up to and including dueAt, pay principal + interest; lender gets it minus 10% of interest; collateral returns to the borrower in the same transaction. Anyone may repay on a borrower's behalf. - Claim: after dueAt, lender takes the escrowed stock; loan closes as Claimed. No grace period, no partial claims. - Admin: owner can allowlist collateral for new offers, set fee up to 20% of interest, set treasury, pause new offers and takes. Owner cannot touch escrow, block a repayment or block a claim. No proxy, no upgrade. - Risks: borrower loses collateral if late; lender receives stock not dollars if the borrower walks; stock tokens are issued by Robinhood and can be paused or an address blocked by the issuer. ## Live stats (at render time) { "open_offers": "0", "open_liquidity": "0", "loans": "0", "lent": "0", "active": "0" } ## Open offers (at render time, first 4000 chars) { "offers": [] } ## Prices used by the app (display only, DexScreener deepest USDG pool per token) { "prices": { "SOUN": { "usd": 6.45, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.806Z" }, "ASTS": { "usd": 62.48, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.810Z" }, "F": { "usd": 14.11, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.810Z" }, "FIG": { "usd": 22.86, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.811Z" }, "SOFI": { "usd": 17.0065, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.812Z" }, "IBM": { "usd": 241.017, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.812Z" }, "APLD": { "usd": 25.84, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.813Z" }, "RKLB": { "usd": 64.12, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.820Z" }, "NET": { "usd": 311.19, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.836Z" }, "UPS": { "usd": 97.93, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.860Z" }, "ZM": { "usd": 94.62, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.872Z" }, "SMCI": { "usd": 37.44, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.878Z" }, "DDOG": { "usd": 221.99, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.891Z" }, "AAOI": { "usd": 105.36, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.906Z" }, "RUN": { "usd": 8.52, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.910Z" }, "NOW": { "usd": 128.51, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.916Z" }, "NBIS": { "usd": 215.4, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.917Z" }, "GLXY": { "usd": 24.28, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.918Z" }, "FUTU": { "usd": 113.27, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.919Z" }, "ADBE": { "usd": 249.76, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.920Z" }, "LMT": { "usd": 531.14, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.930Z" }, "ELF": { "usd": 97.1, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.932Z" }, "IREN": { "usd": 43.82, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.933Z" }, "CCL": { "usd": 22.32, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.935Z" }, "HPE": { "usd": 59.56, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.946Z" }, "IONQ": { "usd": 36.54, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.947Z" }, "CELH": { "usd": 27.53, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.949Z" }, "QBTS": { "usd": 17.11, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.951Z" }, "LITE": { "usd": 912.9, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.951Z" }, "POET": { "usd": 8.0071, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.952Z" }, "SPMO": { "usd": 138.052, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.953Z" }, "RGTI": { "usd": 14.36, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.954Z" }, "CLSK": { "usd": 13.2, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.962Z" }, "MXL": { "usd": 74.25, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.972Z" }, "EWY": { "usd": 186.18, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.976Z" }, "XLK": { "usd": 181.29, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.980Z" }, "SNOW": { "usd": 322.73, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.982Z" }, "LUNR": { "usd": 14.16, "source": "dexscreener", "updatedAt": "2026-09-13T11:55:40.989Z" }, ## Full collateral allowlist (194 tokens) NVDA: 0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC SPCX: 0x4a0E65A3EcceC6dBe60AE065F2e7bb85Fae35eEa AAPL: 0xaF3D76f1834A1d425780943C99Ea8A608f8a93f9 TSLA: 0x322F0929c4625eD5bAd873c95208D54E1c003b2d GOOGL: 0x2e0847E8910a9732eB3fb1bb4b70a580ADAD4FE3 SPY: 0x117cc2133c37B721F49dE2A7a74833232B3B4C0C MSFT: 0xe93237C50D904957Cf27E7B1133b510C669c2e74 AMZN: 0x12f190a9F9d7D37a250758b26824B97CE941bF54 AMD: 0x86923f96303D656E4aa86D9d42D1e57ad2023fdC PLTR: 0x894E1EC2D74FFE5AEF8Dc8A9e84686acCB964F2A GME: 0x1b0E319c6A659F002271B69dB8A7df2F911c153E MU: 0xfF080c8ce2E5feadaCa0Da81314Ae59D232d4afD META: 0xc0D6457C16Cc70d6790Dd43521C899C87ce02f35 COIN: 0x6330D8C3178a418788dF01a47479c0ce7CCF450b INTC: 0xc72b96e0E48ecd4DC75E1e45396e26300BC39681 SNDK: 0xB90A19fF0Af67f7779afF50A882A9CfF42446400 CRWV: 0x5f10A1C971B69e47e059e1dC91901B59b3fB49C3 ORCL: 0xb0992820E760d836549ba69BC7598b4af75dEE03 USAR: 0xd917B029C761D264c6A312BBbcDA868658eF86a6 BE: 0x822CC93fFD030293E9842c30BBD678F530701867 RDDT: 0x05b37Fb53A299a1b874A619e1c4C404D52C36F4C DJT: 0x1D11f0496982706C5e14A514D4E79F2e6BdE4516 NFLX: 0xE0444EF8BF4eD74f74FD73686e2ddF4C1c5591E8 QQQ: 0xD5f3879160bc7c32ebb4dC785F8a4F505888de68 COST: 0x4EA005168D7F09a7A0Ba9D1DEf21a479950E44C2 GLD: 0xC9a981FEE1F9DEc688bb123ccDeCc63D0deBFC4e CRCL: 0xdF0992E440dD0be65BD8439b609d6D4366bf1CB5 TSM: 0x58FfE4a942d3885bAa22D7520691F611EF09e7AA MSTR: 0xec262a75e413fAfD0dF80480274532C79D42da09 TTWO: 0x5e81213613b6B86EaB4c6c50d718d34359459786 HOODRAT: 0x807A4ECD29a9a7A3Bb27d589e2ef9BA831387C83 USO: 0xa30FA36Db767ad9eD3f7a60fC79526fB4d56D344 SLV: 0x411eFb0E7f985935DAec3D4C3ebaEa0d0AD7D89f RBLX: 0xF0C4BF4C582cb3836e98394b1d4e7B7281101bE8 XOM: 0xf9B46d3D1B22199D4D1025a9cEDB540A33F1a2d5 BB: 0x48E39E56aCdbA37b09020C0b734A613C9a2f100A DELL: 0x941AE714EC6D8130c7B75d67160Ca08f1e7d11Dd AMAT: 0x36046893810a7E7fCE501229d57dc3FC8c8716d0 HIMS: 0xCceE82fE024c36fA15E1005edE3E9e4787e23D09 ASML: 0x47F93d52cBeC7C6D2CfC080e154002370a60dAEA MRVL: 0x62fd0668e10D8B72339BE2DCF7643001688ff13B LLY: 0x8005d266423c7ea827372c9c864491e5786600ea QUBT: 0x59818904ab4cE163b3cE4FfB64f2D6Ca02c434B4 QCOM: 0x0f17206447090e464C277571124dD2688E48AEA9 AVGO: 0x156E175DD063a8cE274C50654eF40e0032b3fbcF AMC: 0x05a3d1Cd21d0C88145E82600E62e7E496e0F222B MRNA: 0x43B07D15cE533bEc5476d70C22a78a1B2B662155 BA: 0x4D21483a44Bf67a86b77E3dA301411880797D452 SKYHY: 0x84CAb63bc87912E71ad199ff14A0bA45de68FeF8 NU: 0x408c14038a04f7bD235329E26d2bf569ee20e250 SGOV: 0x92FD66527192E3e61d4DDd13322Aa222DE86F9B5 WYFI: 0x9e7ABD3C9139D14E4c86DcE0e455AAB7A0C2FB3E RIVN: 0xB1BF26c1D20ff267A4f93550d1E0d06ac40a114B BABA: 0xad25Ac6C84D497db898fa1E8387bf6Af3532a1c4 LULU: 0x4e62068525Ab11FE768e29dfD00ef909B9803016 BULL: 0xceF9027c7d6985b85f0BA431125073529A947A68 JNJ: 0x03DfbBE0AC4E7bCDaFd08eD41A400326B77D8c80 KSS: 0x12e3c047bf9AeCAF9dDC98c05C31BFD1dd043993 PENG: 0x9b23573b156B52565012F5cE02CDF60AFBaa70Be P: 0x1Cdad396DB64BDa184d5182A97Dd9B3C62100b7D RCAT: 0xFDE6b5d9BB419B10C23268c74e369AbFF39C0460 SHOP: 0xF53F66751B1Eff985311b693531E3290F600c410 SOUN: 0x6E3Dfd9f7e1649BaA14D25cac18C94d62dB10A54 SNAP: 0xF6589F11Bc40b669e584073F428B05562F568733 ASTS: 0x1AF6446f07eb1d97c546AFC8c9544cBDF3AD5137 F: 0x25C288E6D899b9BC30160965aD9644c67e73bE0C FIG: 0x41F4267525a8AFf329540eF24fD83d9044758B33 SOFI: 0x98E75885157C80992A8D41b696D8c9C6Fb30A926 IBM: 0x980dcf6766FA79f5Cf0c4AAdb3ab477ff15a9619 APLD: 0xb8DBf92F9741c9ac1c32115E78581f23509916FD RKLB: 0x3b14C39E89D60D627b42a1A4CA45b5bb45Fc12e2 NET: 0x116F00968269B7bfbaD4109cE591d6E74c0601d4 PFE: 0x7066A64c24e4206CD62E83bf198c1E7EB361F51e UPS: 0xf23250dac154D05Bb671CB0d0eBEf3c635c79CE2 ZM: 0x44c4F142009036cF477eD2d09932051843137CF1 SMCI: 0xc01aA1fECeC0605b13bc84874ff7256C0f5F562a NNE: 0xBEF75684C43c4ea7BD18Dd532a2244674Ee8b926 DDOG: 0x27c99fBde9D0d2AA4f4Bfb4943f237843DdF6958 AAOI: 0x521Cf887E6531c6F667b5BC4D896E5d9bfE8EB2E RUN: 0x756Bc80af765C82da966a788858d65aDF14f3793 NOW: 0x0C3260aF4B8f13a69c4c2dFb84fD667890CDFa14 NBIS: 0x9D9c6684F596F66a64C030B93A886D51Fd4D7931 GLXY: 0x2D427692E928fa156ec22acfaBaFA0447C5805B7 FUTU: 0xeB30663bDFf0622Ef4e4E5cBb4E975F19f33f51D ADBE: 0x232B8ed6377BE97813853B0Ac104c4Cda8378d1B LMT: 0x329fcACEb9AD6F9580DD5F643fed0646900D043c ELF: 0x39EC44Bee4F6A116c6F9B8De566848a985C53C60 IREN: 0xF0AB0c93bE6F41369d302e55db1A96b3c430212D CCL: 0x9651342CeA770aE9a2969Ba2A52611523146aef9 GE: 0x63b814DDBd6BF339f25Fed8c36158a008D5B373e HPE: 0x59dd09d4900C2E4B5F75b7c0d4E6796fcc234Cb1 IONQ: 0x558378E000D634A36593E338eBacdd6207640EfE CELH: 0x8cF07C5A878945185d327aAa6e33FAa95F95e7bF QBTS: 0xC583c60aeF9Dc401Da72cEC1B404743a93cea1Cc LITE: 0x8eF20885F94e3D9bc7eB3080279188Bd5ED7c08C POET: 0xcf6B2D875361be807EAfa57458c80f28521F9333 SPMO: 0xAd622320e520de39e72d41EF07438C3Fd3354875 RGTI: 0x284358abc07F9359f19f4b5b4aC91901Be2597Ba TEM: 0xB1CC0EC7Db69Cf43539119814df40071b9d61793 CLSK: 0xcBB95BBF36099d34dA091dc6Fa6F49EfA257Cee3 MXL: 0x48961813349333209994750ffA89b3c5C22eC969 EWY: 0x7f0aBeF0C07280F82c6a08ead09dEd6BAE2C13Fc XLK: 0x15Cd20759CE7F3285c29A319dE2D1A2e098c6f43 SNOW: 0xBa0CAB75495255d0cB58E22B648bFED4ECD1F47E LUNR: 0xa5D4968421bA94814Be3B136b15cf422101aC1a3 FLNC: 0x282e87451E10fA6679BC7D76C69BE44cD3fC777C APP: 0xA249BAF1063Af884807C1E1400AEf7784836917E VST: 0x561e2a49212b7cCF47f2744Ccb83e200722fADBc WDAY: 0x82DA4646242e1D962e96e932269Dc644c94a9CaA SOXX: 0x75742c18BC1f1C5c5f448f4C9D9C6F66dafAAa38 CBRS: 0x5c90450Bbb4273D7b2f17CF6917AEB237A569679 RDW: 0x92Ef19E82bD8fF36661DE838D5eaE7e5CEF0EfFE GLW: 0x7c04E6A3368F2A1DE3874f0e80d2e0A1a9915da6 UNH: 0xcF364ea52787e289De6F32077834056E3E70D6A8 WULF: 0x348Be1A8663f15edDe5CDf8A96BB69078f7aB6Fd CRWD: 0xea72Ecca2d0f6bFA1394DBBCff85b52CD4233931 ON: 0xbBD09F72b025360FeE5C928053Dca6248d35be54 IBRX: 0x7c148F74ac7445D1F28366b7FcDC6792a9Fcd0Cf CLOV: 0x62200915e7DEab1eC7f79fb246daDbB80eACdDd0 CVNA: 0xa4f319104089FE321dc8093C6E707d4fE190A988 XNDU: 0xA8eB3BCcbf2017eE7CBfb652eB51CF2E1B153289 FLY: 0x03BC731Ffb162cdd7B98D3C6542bFC291126075d CSCO: 0xF543967EEBB6f1917992eF0E68De63ab07a5a0dA SMR: 0x1Eebee7F74517e0279dFb09d25B0407bEEc3FDd6 TE: 0xb1969f6604CA1AE7a2cD3F1827876e914594CA2D INOD: 0xf1953DAB6FaD537488d5A022361FfAa8B4c95eC6 INTU: 0x56d23beE5f41A7120170b0c603Dae30128e460e9 NVTS: 0xbE6702d7b70315376dC48a3293f24f0982F86386 MDB: 0xDdf2266b79abf0B48898959B0ed6E6adf512be74 INDA: 0xACEF2e09adb47aD6aBeBAD9fF06689E60615C2B6 ZS: 0x7dc013eB55e436f30d7ED1AFE4E36d6e45e3c3f7 PR: 0x4189F0c66EBBB0bfeF1C31f763131361EF32f77C WDC: 0xF52597345A8Edf418bc4071b4a35112472277D3e TSEM: 0x89776d4Cd68193597A2fC132cfaC1fDe36CCeA8a TTD: 0x0b5fb4031cae9163db10B169Ee72685F0EdC8545 UMC: 0x0E6e67Ba88e7b5d9B67636A215c76779B948dE79 CRM: 0xd95B44124e475743a7589e68F3D74008A5536D44 JOBY: 0xb334C5cE741B80B5B671F47F5C269Cb193fe8E24 AMBA: 0x99D9D8663545151603863C5AcbD6FC3218899009 PATH: 0xfb2664f07B6Aadd29ea7a59D8859b1AeB8645cDa PL: 0xAA4d64474c172010aB57719cb9951E6142a100d3 AVAV: 0xF6290b5e7C26502e2dA514C31509849718EA76A5 TER: 0x2778C5024D5cA2CdB0f8eAD671ffc69963AdCD9C OKLO: 0x8B2f88497f15A18E9D4FFa1a8fFB8538399aE774 AUR: 0x373C06c4f7BDe527D7Dae4BA169E42b55E393CeD LRCX: 0x57b0030166DB0C31690d1A5aA167e2e26e2C29a4 CIEN: 0x44f6D488021f8233B9416294d1FE9b1fEe28382d KTOS: 0x7FD06a4d81cCfA3F351394E144d5191874C31313 TEAM: 0x5B97476b922F3305131B8f0B9D333172E87f4aaE CEG: 0xaE517A2903E68bd929Dfd15be875F8369D53e94a AMKR: 0xDd356AA38F40A7b7076755aC854B6FBb1F0D305B VSAT: 0x26dCbfb34FC83CAbD6990f449674efDc6097fF85 VTI: 0x0594134DF3f171a354D9C85eBD65b7A6148F6D09 DOCN: 0xc02f12B9fe9E707079EC0d546f3050d3F6C1F8bD SMH: 0x072f979c2CAc8e1391B0162a87Fee094bF8744a0 COHR: 0x92F9F459F1a9a5AD266b182BE7Bffd1C6c666894 PANW: 0xB039597eD45CBa7B6E2fb9E8BE51802969CEe5Be FTNT: 0x3FB8976980d486084b2eb4a404BD12e72823958f ALAB: 0x748c32c3ca24eDf31ea597Db1F3d330a7a6DA3Dc VRT: 0xFA78C12E6488814A0262E4e802749a4a737d5fB7 ANET: 0x28bABD556b60E53663B8615036479a29c2CDd1Bf POWL: 0x237c16D66590F67B886d978ACD362EAeaD8B18c7 FISV: 0x9ECe29A4A2397C0a35fb5fA8EE2b9509130a98cc ABCL: 0x3139D77Ace0cbAA5bDfD38bD1F1911a794AF0B0e INFQ: 0xB853bC83a753342a4f8320ea680b4B1E84118D21 SLS: 0x285b231728c7E4333799183DF1094d775246a535 AEHR: 0x5F604fBA1162193A4388A5DFa56F556f3E133cC2 JBL: 0xEAf2512dFC1bEAc608F8794B3793CD4E02894Aa6 AXON: 0xC27dBD474aF5181c5A8777903690D8D262D12648 SIMO: 0x77E655E37F4d913fB9540e0d541D824171a60e81 NAVN: 0xf7181b63Fdb858558A74ba96BC42732684cd7965 MOD: 0xc6Cbad1016b38B797610c25E1dc7D95988B1f362 CLS: 0xBf449977089c718C004a66C554B26B94ef3Ad4De MTSI: 0xC93f4d80e268AB922e871bd169156C3CC41894e6 HII: 0xEB61c0Ed490A367d4E3631cCf8a74B3bfc7E775D FICO: 0xa48F22A46C0F1C46CA7D111CB6c137c271987180 CRDO: 0x4D67253bc223e6b0e104F1084c1fb2b669dDC41b CTSH: 0x63D5a3b6939a33f1e75d8Bcd85759858239600DB FIX: 0x93Dbb1d2Dc5D63F4abACFF30485273f538Df68Ac AXTI: 0x141eEa040c2250eEc0314e336975e81f85f6585e VICR: 0x6006ed4B2F94110851ff7509D97D034f0EeD9226 OUST: 0x40E7a279850e443f582059ae5dC1c3b6563E6395 GEV: 0x94B8AAE43A1cCc08Aa64B7D1F29b4D920aF4a0C9 HWM: 0xAEa445c5F3DB1a462998ccC422A875A361ee5d99 LHX: 0x48d60243c66437c6ac3c2495Be94747aEd5Dfe25 AEIS: 0xfAf9cb261B5FCC1f404Bb10CD39C5c6C1974E612 SCHD: 0xd63ABB2C13d7a8421a8017a712802053568e3C1D ONTO: 0x8ff63eAeEe3fE54Ba450c4F5538064Ec5A893Aef SHY: 0xBE274710Bf3d9567e1B290eF6a5F9f90ca016FD8 EWT: 0x1c690498150252222C275A5CEd69d3A6b1f52D5E KLAC: 0x96b933C74eCB4A0926b9210cef7b743EF46be2E9 MPWR: 0x52D50D0280AD1054b43f052bD70a49a212A1b128 PWR: 0x9Ab02Ead789b6903c3c44d0ED32F9c707CDF12FD SATS: 0x95052ddcd5DC25641657424A8Cf04834997E1730 ## ABI (human readable) createOffer(address collateral, uint128 amount, uint128 minTake, uint64 term, uint32 aprBps, uint256 collateralPerUnit, uint64 expiry) returns (uint256 offerId) cancelOffer(uint256 offerId) take(uint256 offerId, uint128 principal) returns (uint256 loanId) repay(uint256 loanId) claim(uint256 loanId) quoteCollateral(uint256 offerId, uint256 principal) view returns (uint256) quoteInterest(uint256 offerId, uint256 principal) view returns (uint256) offersCount() view returns (uint256) loansCount() view returns (uint256) offersOf(address lender) view returns (uint256[]) loansOf(address user) view returns (uint256[]) getOffers(uint256 from, uint256 count) view returns ((address lender,address collateral,uint128 available,uint128 minTake,uint64 term,uint32 aprBps,uint64 expiry,uint256 collateralPerUnit,bool cancelled)[]) getLoans(uint256 from, uint256 count) view returns ((address borrower,address lender,address collateral,uint128 principal,uint128 interest,uint256 collateralAmount,uint64 startedAt,uint64 dueAt,uint256 offerId,uint8 state)[]) collateralAllowed(address) view returns (bool) stable() view returns (address) feeBps() view returns (uint256) treasury() view returns (address) events: OfferCreated, OfferCancelled, OfferTaken, LoanRepaid, LoanClaimed ## Using it from a shell cast send 0xe4FFED90e5EABFB12B068Da673607514F9C6f469 "take(uint256,uint128)" 0 500000000 --rpc-url https://rpc.mainnet.chain.robinhood.com/rpc --private-key $KEY cast send 0xe4FFED90e5EABFB12B068Da673607514F9C6f469 "repay(uint256)" 3 --rpc-url https://rpc.mainnet.chain.robinhood.com/rpc --private-key $KEY ## OakLoans.sol (deployed source, verbatim) // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; /// @title OakLoans: fixed-term, peer-to-peer loans against tokenized stocks. No oracle, no liquidations. /// /// A lender posts an offer: stablecoin they will lend, which stock token they accept, how much of it /// they want per stablecoin unit (their own price, their own LTV), the term, and the APR. A borrower /// takes part of an offer: the stock goes into escrow here, the stablecoin goes to the borrower, and /// the interest is fixed in stablecoin units at that moment. Before the due date the borrower repays /// principal plus interest and gets the stock back. After the due date the lender can claim the stock. /// Prices never move a loan; only the date matters. contract OakLoans is Ownable2Step, ReentrancyGuard { using SafeERC20 for IERC20; uint256 public constant BPS = 10_000; uint256 public constant MAX_FEE_BPS = 2_000; // 20% of interest, hard cap uint256 public constant MIN_TERM = 1 days; uint256 public constant MAX_TERM = 365 days; struct Offer { address lender; address collateral; // stock token accepted uint128 available; // stablecoin still lendable uint128 minTake; // smallest principal a borrower may take uint64 term; // seconds uint32 aprBps; // simple interest, annualised uint64 expiry; // offer cannot be taken after this timestamp uint256 collateralPerUnit; // collateral wei per 1 stablecoin unit (10**stableDecimals) bool cancelled; } enum LoanState { None, Active, Repaid, Claimed } struct Loan { address borrower; address lender; address collateral; uint128 principal; uint128 interest; uint256 collateralAmount; uint64 startedAt; uint64 dueAt; uint256 offerId; LoanState state; } IERC20 public immutable stable; uint256 public immutable stableUnit; // 10**decimals of the stablecoin address public treasury; uint256 public feeBps; // share of interest taken on repayment bool public offersPaused; // pauses new offers and takes only; repay and claim always work mapping(address => bool) public collateralAllowed; address[] public collateralList; Offer[] public offers; Loan[] public loans; mapping(address => uint256[]) private _offersOf; mapping(address => uint256[]) private _loansOf; event CollateralSet(address indexed token, bool allowed); event FeeSet(uint256 feeBps, address treasury); event OffersPaused(bool paused); event OfferCreated(uint256 indexed offerId, address indexed lender, address indexed collateral, uint256 amount, uint256 minTake, uint64 term, uint32 aprBps, uint256 collateralPerUnit, uint64 expiry); event OfferCancelled(uint256 indexed offerId, uint256 returned); event OfferTaken(uint256 indexed offerId, uint256 indexed loanId, address indexed borrower, uint256 principal, uint256 interest, uint256 collateralAmount, uint64 dueAt); event LoanRepaid(uint256 indexed loanId, uint256 principal, uint256 interest, uint256 fee); event LoanClaimed(uint256 indexed loanId, uint256 collateralAmount); error NotAllowed(); error BadInput(); error Paused(); error NotLender(); error NotBorrower(); error OfferClosed(); error TooSmall(); error NotActive(); error TooEarly(); error TooLate(); constructor(IERC20 stable_, uint8 stableDecimals, address owner_, address treasury_, uint256 feeBps_) Ownable(owner_) { if (address(stable_) == address(0) || treasury_ == address(0) || feeBps_ > MAX_FEE_BPS) revert BadInput(); stable = stable_; stableUnit = 10 ** stableDecimals; treasury = treasury_; feeBps = feeBps_; } // ---------------------------------------------------------------- admin function setCollateral(address token, bool allowed) external onlyOwner { if (token == address(0) || token == address(stable)) revert BadInput(); if (allowed && !collateralAllowed[token]) collateralList.push(token); collateralAllowed[token] = allowed; emit CollateralSet(token, allowed); } function setFee(uint256 feeBps_, address treasury_) external onlyOwner { if (feeBps_ > MAX_FEE_BPS || treasury_ == address(0)) revert BadInput(); feeBps = feeBps_; treasury = treasury_; emit FeeSet(feeBps_, treasury_); } function setOffersPaused(bool paused) external onlyOwner { offersPaused = paused; emit OffersPaused(paused); } // ---------------------------------------------------------------- lender /// @notice Post stablecoin to lend against one stock token at a price you set. /// @param collateralPerUnit stock token wei required per one whole stablecoin (e.g. per 1 USDG). function createOffer(address collateral, uint128 amount, uint128 minTake, uint64 term, uint32 aprBps, uint256 collateralPerUnit, uint64 expiry) external nonReentrant returns (uint256 offerId) { if (offersPaused) revert Paused(); if (!collateralAllowed[collateral]) revert NotAllowed(); if (amount == 0 || minTake == 0 || minTake > amount || collateralPerUnit == 0) revert BadInput(); if (term < MIN_TERM || term > MAX_TERM || aprBps > BPS * 5) revert BadInput(); if (expiry <= block.timestamp) revert BadInput(); stable.safeTransferFrom(msg.sender, address(this), amount); offerId = offers.length; offers.push( Offer({ lender: msg.sender, collateral: collateral, available: amount, minTake: minTake, term: term, aprBps: aprBps, expiry: expiry, collateralPerUnit: collateralPerUnit, cancelled: false }) ); _offersOf[msg.sender].push(offerId); emit OfferCreated(offerId, msg.sender, collateral, amount, minTake, term, aprBps, collateralPerUnit, expiry); } /// @notice Withdraw whatever is still unlent. Loans already taken from the offer are untouched. function cancelOffer(uint256 offerId) external nonReentrant { Offer storage o = offers[offerId]; if (o.lender != msg.sender) revert NotLender(); if (o.cancelled) revert OfferClosed(); o.cancelled = true; uint256 back = o.available; o.available = 0; if (back > 0) stable.safeTransfer(msg.sender, back); emit OfferCancelled(offerId, back); } /// @notice After the due date, the lender takes the escrowed stock. The loan is closed. function claim(uint256 loanId) external nonReentrant { Loan storage l = loans[loanId]; if (l.state != LoanState.Active) revert NotActive(); if (l.lender != msg.sender) revert NotLender(); if (block.timestamp <= l.dueAt) revert TooEarly(); l.state = LoanState.Claimed; IERC20(l.collateral).safeTransfer(l.lender, l.collateralAmount); emit LoanClaimed(loanId, l.collateralAmount); } // ---------------------------------------------------------------- borrower /// @notice Borrow `principal` stablecoin from an offer. Collateral is pulled from you; know the numbers first: /// collateral = ceil(principal * collateralPerUnit / stableUnit), interest = principal * aprBps * term / (365d * BPS). function take(uint256 offerId, uint128 principal) external nonReentrant returns (uint256 loanId) { if (offersPaused) revert Paused(); Offer storage o = offers[offerId]; if (o.cancelled || o.available == 0 || block.timestamp > o.expiry) revert OfferClosed(); if (principal == 0 || principal > o.available) revert BadInput(); // the remainder must stay takeable, or the whole thing must go if (principal < o.minTake && principal != o.available) revert TooSmall(); if (o.available - principal != 0 && o.available - principal < o.minTake) revert TooSmall(); if (!collateralAllowed[o.collateral]) revert NotAllowed(); uint256 collateralAmount = quoteCollateral(offerId, principal); uint128 interest = uint128(quoteInterest(offerId, principal)); o.available -= principal; IERC20(o.collateral).safeTransferFrom(msg.sender, address(this), collateralAmount); stable.safeTransfer(msg.sender, principal); uint64 dueAt = uint64(block.timestamp) + o.term; loanId = loans.length; loans.push( Loan({ borrower: msg.sender, lender: o.lender, collateral: o.collateral, principal: principal, interest: interest, collateralAmount: collateralAmount, startedAt: uint64(block.timestamp), dueAt: dueAt, offerId: offerId, state: LoanState.Active }) ); _loansOf[msg.sender].push(loanId); _loansOf[o.lender].push(loanId); emit OfferTaken(offerId, loanId, msg.sender, principal, interest, collateralAmount, dueAt); } /// @notice Repay principal plus the fixed interest any time up to and including the due date. Collateral comes back. function repay(uint256 loanId) external nonReentrant { Loan storage l = loans[loanId]; if (l.state != LoanState.Active) revert NotActive(); if (block.timestamp > l.dueAt) revert TooLate(); l.state = LoanState.Repaid; uint256 fee = (uint256(l.interest) * feeBps) / BPS; uint256 toLender = uint256(l.principal) + l.interest - fee; // anyone may repay on the borrower's behalf; the collateral always returns to the borrower stable.safeTransferFrom(msg.sender, address(this), uint256(l.principal) + l.interest); stable.safeTransfer(l.lender, toLender); if (fee > 0) stable.safeTransfer(treasury, fee); IERC20(l.collateral).safeTransfer(l.borrower, l.collateralAmount); emit LoanRepaid(loanId, l.principal, l.interest, fee); } // ---------------------------------------------------------------- views function quoteCollateral(uint256 offerId, uint256 principal) public view returns (uint256) { Offer storage o = offers[offerId]; return (principal * o.collateralPerUnit + stableUnit - 1) / stableUnit; } function quoteInterest(uint256 offerId, uint256 principal) public view returns (uint256) { Offer storage o = offers[offerId]; return (principal * o.aprBps * o.term) / (365 days * BPS); } function offersCount() external view returns (uint256) { return offers.length; } function loansCount() external view returns (uint256) { return loans.length; } function collateralCount() external view returns (uint256) { return collateralList.length; } function offersOf(address lender) external view returns (uint256[] memory) { return _offersOf[lender]; } function loansOf(address user) external view returns (uint256[] memory) { return _loansOf[user]; } function getOffers(uint256 from, uint256 count) external view returns (Offer[] memory out) { uint256 n = offers.length; if (from >= n) return out; uint256 to = from + count > n ? n : from + count; out = new Offer[](to - from); for (uint256 i = from; i < to; i++) out[i - from] = offers[i]; } function getLoans(uint256 from, uint256 count) external view returns (Loan[] memory out) { uint256 n = loans.length; if (from >= n) return out; uint256 to = from + count > n ? n : from + count; out = new Loan[](to - from); for (uint256 i = from; i < to; i++) out[i - from] = loans[i]; } }