<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>想住大房子的java程序员</title>
        <link>https://paragraph.com/@java-3</link>
        <description>undefined</description>
        <lastBuildDate>Mon, 13 Apr 2026 06:03:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>想住大房子的java程序员</title>
            <url>https://storage.googleapis.com/papyrus_images/ac21991037cb79dad4660875d3e30b6079350b8617a929dd58397e72df86d638.jpg</url>
            <link>https://paragraph.com/@java-3</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Solana的与众不同]]></title>
            <link>https://paragraph.com/@java-3/solana</link>
            <guid>n5eGxuc8lWHDvJSb11ow</guid>
            <pubDate>Fri, 12 Jan 2024 06:26:38 GMT</pubDate>
            <description><![CDATA[转自推特gm365老哥 对于一个长期浸淫在 EVM （以太坊、L2、BSC、Avax等）世界的人，刚切换到 Solana 生态时，除了要换一个浏览器插件钱包、交易更便宜外，可能你会觉得两条公链差别也不是很大 但一番深入研究下来，发现二者从底层设计理念，到各个层面，几乎天差地别 这里将我前期学习了解的一些特性加以总结，帮你更快速入门 Solana ，少走一些弯路普通用户对普通用户而言， Solana 的实际差别不会特别大，具体表现可能在下面几个方面： 1、浏览器插件钱包 MetaMask 不直接支持 Solana, 你得换用 Phantom 之类，问题不大 但是，通过 MetaMask Snap 特性，安装 solflare 插件，就可以继续使用 MetaMask 了 2、交易费用极低 相对于以太坊主网动辄几刀、十几刀的 gas 费，L2 (Arbitrum, OP, zkSync Era等)上大约 $0.2 左右的 gas，即便 BNB Chain 也要 $0.1，但 Solana 上一笔交易甚至不足 $0.01，着实令人印象深刻 3、交易“偶尔”会失败 如果你用的次数较多，项目...]]></description>
            <content:encoded><![CDATA[<p>转自推特gm365老哥</p><p>对于一个长期浸淫在 EVM （以太坊、L2、BSC、Avax等）世界的人，刚切换到 Solana 生态时，除了要换一个浏览器插件钱包、交易更便宜外，可能你会觉得两条公链差别也不是很大</p><p>但一番深入研究下来，发现二者从底层设计理念，到各个层面，几乎天差地别</p><p>这里将我前期学习了解的一些特性加以总结，帮你更快速入门 Solana ，少走一些弯路</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">普通用户</h2><p>对普通用户而言， Solana 的实际差别不会特别大，具体表现可能在下面几个方面：</p><p>1、浏览器插件钱包</p><p>MetaMask 不直接支持 Solana, 你得换用 Phantom 之类，问题不大</p><p>但是，通过 MetaMask Snap 特性，安装 solflare 插件，就可以继续使用 MetaMask 了</p><p>2、交易费用极低</p><p>相对于以太坊主网动辄几刀、十几刀的 gas 费，L2 (Arbitrum, OP, zkSync Era等)上大约 $0.2 左右的 gas，即便 BNB Chain 也要 $0.1，但 Solana 上一笔交易甚至不足 $0.01，着实令人印象深刻</p><p>3、交易“偶尔”会失败</p><p>如果你用的次数较多，项目较广，会发现 Solana 交易有一定的失败概率，绝对比你使用 EVM 链多</p><p>4、交易速度“挺快”</p><p>相比以太坊的平均十几秒、几十秒，L2 的几秒， Solana 的交易确认速度算是比较快的了，实际感受大约在几秒，但并不太稳定</p><p>目前依然比不上 Arbitrum 的几乎秒确认</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">开发者</h2><p>5、Solana 的开发语言</p><p>Solana 的智能合约（他们叫 Program) 使用 Rust 或者 C 开发，不用 Solidity</p><p>6、区块间隔</p><p>Solana 的区块（他们叫 Slot）间隔是 400ms(目标值），实际值大约在 400～500 ms 之间</p><p>7、TPS</p><p>Solana 区块浏览器看到的 TPS 在 3000 左右，令人印象深刻</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/834a3ac21888794e33719a98f06bb1893770665fe3c3e245c9971f576ac1c9db.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>然而，这里面有大量的交易只是 Vote 这样的“无效交易”，真实的用户交易占比约 25%，也就是 700+ TPS，依然吊打一众 EVM 兼容链</p><p>8、Nonce</p><p>Solana 没有 Nonce 的概念，这是 EVM 上才有的“落后特性”</p><p>按照官方文档，Nonce 的设置，利好负责区块打包的一方，但严重不利于普通用户（想想被一堆 Pending 交易支配、需要手工挨个加速的恐惧）</p><p>9、共识机制</p><p>Solana 给予 PoH 共识机制，根据时间戳决定区块顺序</p><p>10、交易过期</p><p>用户提交的每笔交易，都包含最新的区块哈希（时间戳），交易会在大约150个区块后（大约1分20秒左右）自动过期（被节点自动丢弃）</p><p>11、并行处理</p><p>Solana 对所有待打包的交易并行处理，效率极高，速度极快。</p><p>如果某两笔交易有冲突，则将冲突交易放入下一个区块处理</p><p>12、合并多笔交易</p><p>Solana 默认支持在一条 tx 中插入多笔交易，一次性提交和处理</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">进阶用户</h2><p>13、合约开源</p><p>Solana 的合约（Program）默认都是“不开源”，你在 solscan 这类区块浏览器无法看到任何合约源代码相关信息</p><p>想要源码？除非官方主动公开在 GitHub 上</p><p>除此之外，根据项目方公布的源码重新编译，并不能得到和现有合约一模一样的“BPF字节码”（貌似和编译机制有关，编译时会有轻微随机变动）</p><p>但似乎关心这事的人并不太多，普通用户不关心，开发者似乎也集体噤声</p><p>14、合约与数据分离</p><p>Solana 的合约与数据是彻底分开存放的，这样保证了合约的执行是无状态的，从而实现并行处理</p><p>15、租金</p><p>你每买入一个代币/NFT，都需要在你的钱包 Wallet 开设一个单独的新账户 account，并支付一定的租金</p><p>你也可以关闭账户并拿回租金，目前价值约 $0.2</p><p>也就是说，如果买入100种不同的代币，租金你就得付 $20</p><p>16、一键发币</p><p>Solana 号称对开发者极其友好，文档齐全</p><p>体现在哪儿呢？</p><p>安装好官方的 SPL-Token 命令行工具，只需要下面一行命令，就可以自动部署一个代币（NFT同理）</p><p>spl-token create-token</p><p>17、代币名字？</p><p>你发的代币想要有名字和介绍？</p><p>这需要一个第三方工具 metaplex 支持</p><p>P.S. 由于这个特性，默认情况下，你是可以修改一个代币的名称、符号和介绍内容的，除非你主动放弃这个权限</p><p>18、“阉割版”区块浏览器</p><p>由于无法在区块浏览器验证合约源代码，你无法通过区块浏览器做下面这两件事：</p><p>a、读取合约代码 b、读取合约函数的值 c、写入（调用）合约</p><p>19、SPL-20 添加流动性</p><p>想要在最大的 DEX Raydium 上给你的 SPL-20 代币添加流动性，你需要：</p><p>a、在他们网站提交申请 b、支付费用，大约 3-4 SOL ，这可是 $300~ $400 c、等待被批准 d、添加流动性，等待开启交易（大约 5-10分钟）</p><p>这和 UniSwap 所谓的“无需许可”（自然也无需额外付费）的添加流动性池方法，天差地别</p><p>据 DeFiLlama 数据，过去7天 Raydium 收益 $8700万（我猜应该还没包括这笔上币费）</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">整体感触</h2><p>作为一个长期使用 EVM 兼容链的人来说，发现 Solana 这些稀奇古怪的特性，有震惊，有不解，有赞叹</p><p>如果说以太坊自身的成功导致其“积重难返”，几乎不可能对底层机制做颠覆性调整，从而导致其可能长期无法解决 TPS 和交易费用过高的问题</p><p>那么，在以太坊 EVM 基础上发展而来的几乎所有 EVM兼容链、L2，恐怕在出生那刻就带着先天不足，是戴着镣铐跳舞</p><p>L3 套娃 L2 套娃 L1 的模式，真能彻底解决这个问题？我个人目前持存疑态度</p><p>Solana 由于彻底抛弃 EVM 这个历史包袱，从头设计的颠覆性创新，给了普通用户更快、更便宜、更好的 L1 公链体验</p><p>未来用户会作何选择？拭目以待</p><p>以上就是对近期学习 Solana 的一些知识点汇总，希望对你有所帮助 ❤️</p><p>20、Solana 交易前不需要授权 由于设计理念的差别，在 Solana 上进行交易（比如 DEX Swap, Deposit 等），不需要像 EVM 上那样，先授权再交易 也鉴于此，Solana 上（似乎）不存在因为合约授权导致的资产被盗 但是，Solana 由于一笔 tx 可以一次性提交多笔交易，导致如果遇到钓鱼手法，可以在一条 tx 内，盗取你钱包内所有资产，包括原生资产 SOL。所以务必小心，钱包提交签名前，看清具体签名内容</p><p>21、Solana 的“开户费” Solana 程序和数据分离的特性，逻辑和 EVM 完全不同。由此导致你需要经常性支付一笔小额“开户费”， SPL-20 的代币，开户费大约 0.002 SOL, NFT 贵一些，大约 0.005 SOL 也由此，如果你要给别人账户发送一个土狗币/NFT，假如对方账户未曾开立此账户（每个不同的币，都需要开设一个单独账户），那么在你发送土狗币时，也要由你支付这笔开户费 而对方可以选择 close account 拿回这笔开户费（其实是租金） 所以，想要给1万个账户发送土狗币，再关闭账户“赚租金”这种套利的好事，是不存在的</p><p>a: @StepFinance_ 收费 5%，有点类似Solana 生态的 DeBank</p><p>b: @solincinerator 收费 0.00004～0.00335 SOL 之间（具体请看对方网站 FAQ 部分，不同类型账户收费不同）</p><p>c: 使用官方 CLI 工具，免费 Burn Tokens &amp; Close Accounts</p><p>注意：Solana 原生支持 Burn 燃烧代币，以及 关闭不需要的账户退租金</p><p>此操作不可逆，务必小心操作</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[2023年区块链掘金总结
]]></title>
            <link>https://paragraph.com/@java-3/2023</link>
            <guid>IL4QuBcecMy1dx8kaqz2</guid>
            <pubDate>Mon, 08 Jan 2024 07:51:57 GMT</pubDate>
            <description><![CDATA[在12月中的时候，是一直期待自己能够写上一篇满意的年终总结的。 但是，Stark没有按照大家的预想在圣诞节之前发放空投。 也知道了一个奇怪的知识：犹太人是不过圣诞节的。 很多时候，市场就是这样子，不断偏离你的预想和期盼，即使你的表情愤怒狰狞直至变形。 生活还是要过下去。1.小牛试刀NFT这个小图片说真的，我对它的偏见是非常之大。无聊猿巅峰之际可是能够达到几百万人民币的价格。 用我当时匮乏的认知来理解，怎么也想不通它为什么值那么多钱。 所以，连带着Blur火热的撸毛我也选择性忽略了，也忽略了泼天的财富。 OP和APT意外的大风刮来的钱让我继续投入了OP奥德赛。 嗯，认知不够果然是赚不到钱的。op奥德赛没有让我赚到钱，也让我错过了最宝贵的空投欢乐季。 在大家都晒出Blur的收益时，让我懊悔了好几天，暗暗发誓不要再有偏见。 在这个阶段结束时，我还是处于懵懵懂懂的状态。2.ARB的空投盛宴3月份的arb空投消息充斥着整个网络，gas在将来的两三个月都是维持在50以上。 圈外的土老板开始入场，各种工作室的接单广告开始频繁出现。 L0和zksync也开始公布自己的融资截胡arb的空投，就差直...]]></description>
            <content:encoded><![CDATA[<p>在12月中的时候，是一直期待自己能够写上一篇满意的年终总结的。</p><p>但是，Stark没有按照大家的预想在圣诞节之前发放空投。</p><p>也知道了一个奇怪的知识：犹太人是不过圣诞节的。</p><p>很多时候，市场就是这样子，不断偏离你的预想和期盼，即使你的表情愤怒狰狞直至变形。</p><p>生活还是要过下去。</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>1.小牛试刀</strong></h3><p>NFT这个小图片说真的，我对它的偏见是非常之大。无聊猿巅峰之际可是能够达到几百万人民币的价格。</p><p>用我当时匮乏的认知来理解，怎么也想不通它为什么值那么多钱。</p><p>所以，连带着Blur火热的撸毛我也选择性忽略了，也忽略了泼天的财富。</p><p>OP和APT意外的大风刮来的钱让我继续投入了OP奥德赛。</p><p>嗯，认知不够果然是赚不到钱的。op奥德赛没有让我赚到钱，也让我错过了最宝贵的空投欢乐季。</p><p>在大家都晒出Blur的收益时，让我懊悔了好几天，暗暗发誓不要再有偏见。</p><p>在这个阶段结束时，我还是处于懵懵懂懂的状态。</p><h3 id="h-2arb" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>2.ARB的空投盛宴</strong></h3><p>3月份的arb空投消息充斥着整个网络，gas在将来的两三个月都是维持在50以上。</p><p>圈外的土老板开始入场，各种工作室的接单广告开始频繁出现。</p><p>L0和zksync也开始公布自己的融资截胡arb的空投，就差直接喊：来吧。赶紧来刷。我很快就会发空投的。</p><p>大家都在依靠过去项目的空投标准判断下一个空投标准。</p><p>当时的我也在刻着ARB的舟，希望自己能够踏上这一趟列车，至少不会再次错过。</p><p>确定了几个项目之后，开始学习脚本开发。</p><p>载着我的期望，一切都是美好的开始。</p><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>3.跨链桥无大毛</strong></h3><p>行业内都是流传着这么一句话：跨链无大毛。</p><p>但是顶不住高融资和全链互通的前景下，大家的热情极度高涨。</p><p>网络教程一篇接着一篇，每日message总数不断新高。</p><p>即使顶着高gas，大家也在害怕从错过。</p><p>同时，项目方github代码被扒出含有airdrop字样和代币的支持。</p><p>达到高潮部分的，是某个KOL说了一个小道消息：L0即将在5月底发放空投。</p><p>市场达到了机制fomo。</p><p>好事者自己制订了一个自我PUA的工具。简介那里写着：主人，请鞭挞我吧！</p><p>到了12月份，项目的每日message降到了冰点。</p><p>L0的项目方开始着急，发了一篇文章，声明自己在2024年的上半年会有代币空投。</p><p>可是，信任就像裂开的镜子，修复起来是很难的。</p><p>感情上的厌恶让我对它重新对它审视了一番，也重新审视了那句话：跨链无大毛。</p><p>如同OP奥德赛一样，直到现在，它仍然没有给我任何的收益。</p><h3 id="h-4zksync" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>4.鸡贼的zksync</strong></h3><p>对zksync的印象不太好，起源于有一次在新加坡还是HK开会时，很明显感觉得到项目方很穷。</p><p>虽然说，有几分钱就不应该把衣服用力抖得啪啪响，但是至少作为一个融资几亿美金的项目来说，确实不应该是这个样子的。</p><p>后来爆出项目方其实没钱，到处融资融不到。在arb发布空投后，马上鸡贼地说自己明年就会发空投。</p><p>这下好了，撸毛用户直接把钱包地址卷到了几百万。假融资变成了真融资。只不过融资方变成了广大撸友。</p><p>在年底的土耳其大会上，明显看得出项目方的精神气不一样的了。</p><p>之所以决定把重心偏离zks，是在stark创始人的一句话上。</p><p>当时主持人问他怎么看待zksync的。</p><p>stark创始人停顿了一下，应该是在组织语言，最后说：他们很懂得营销。同时期待用户被愚弄。</p><p>话里话外都是在说自己技术好。</p><h3 id="h-5" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>5.技术男的坟墓</strong></h3><p>我觉得搞技术的男人都有一种迷之自信。</p><p>觉得技术很酷，只要技术好，市场就会认可。标价9.9和标价10块钱其实是一样的，用户迟早会明白的。</p><p>所以，仔细研究了starknet之后，我觉得把重心放在stark上面。</p><p>即使那个时候，它一笔交易有可能长达二十分钟再确认，但是选择愿意陪他成长，也相信经过量子跃动之后的大升级。</p><p>在不断推出针对开发者的大型空投，而普通用户却丝毫不提的情况下。社区有了数次暴动。</p><p>在空投页面不小心泄露出来的极其苛刻的条件中，用户的不满达到了顶点。</p><p>但是stark官方却是选择了硬刚：没错，前期我们就是对开发者非常慷慨。</p><p>他们坚信，只要有开发者，开发出好的产品，理智的用户肯定会选择stark的。</p><p>而我，本来是可以成为开发者的，不明白为什么我把自己降到了普通用户那一层。</p><h3 id="h-6" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>6.大风刮来的钱</strong></h3><p>如果说很久不见收入的情况下，忽然有人把钱塞到你的口袋里，那就会像阳光刺破了阴霾的天气。</p><p>你会很感激那到阳光的。</p><p>如果说接下来还有几道阳光照射进来，你就会开心一整天的。</p><p>唯一可惜的就是认知不到位，早早卖飞。看到拉盘拉倒你怀疑人生时，计算器算了又算，不由长叹。</p><h3 id="h-7" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>7.仔细研究返贫，无脑梭哈致富</strong></h3><p>在8月9月份时，对于准备燃起的铭文表示很不看好。</p><p>始终有一句横在内心里面：就一串json，就是技术革命？</p><p>鄙视的嘴巴都快要歪到地上去了。</p><p>当暴富神话不断地涌现出来，我有点坐不住了。捏着鼻子花了几十U在matic上面上面几千张pols。</p><p>打完之后，不知道是不是屁股决定脑袋，恨不得它拉到几十上百倍。</p><p>稍作停留之际，居然想着继续打下一个铭文。</p><p>？？？，难道吃翔还会上瘾吗？</p><p>经过一段时间的铭文市场厮杀。pols给我带来了差不多50倍收益。</p><p>最有意思的是，同一份铭文，居然可以在其他市场再交易一次！！！</p><p>--------------</p><h3 id="h-8" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0"><strong>8.勇敢扔掉拐杖</strong></h3><p>如果说，你一直拄着拐杖走路，那么你永远学不会自己走路的。</p><p>你会经历不断摔倒和怀疑。</p><p>但是，只要你不再留恋和依赖拐杖，那么你迟早会跑起来的。</p><p>明年的目标充满了挑战，却也让人很心动。</p><p>所以，给现在的自己几句话，也给迷茫时的自己几句话：</p><p>1.区块链永远不缺叙事，一定要紧跟热点。</p><p>2.时刻保持冷静，不要在一个已亏损的项目上停留太多时间。</p><p>3.往往越简单的越可能成功。就是一坨翔，你也要仔细研究以后再做决定。</p><p>4.不断学习提高自己。每一个知识点都会化作牛市的一把利剑。</p><p>5.看不懂的不要重仓，迷雾中的世界充满了危险。</p><p>6.不要做空，不要做空，不要做空！！！！</p><h2 id="h-2024btcsolana" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0"><strong>最后，2024年，一起梭哈BTC生态，还有solana。</strong></h2>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[比特币历史牛市时间及价格介绍]]></title>
            <link>https://paragraph.com/@java-3/9sNlzOiCk9n2O5OuLREF</link>
            <guid>9sNlzOiCk9n2O5OuLREF</guid>
            <pubDate>Tue, 10 Jan 2023 10:11:51 GMT</pubDate>
            <description><![CDATA[1.第一次牛市时间段： 2010年9月---2011年6月，持续时间9个月。比特币从0.06美元涨到36美元，随后马上跌到2.5美元，跌幅达到93%，不少人说，比特币会死，但以后没有低于2美元。这意味着，在这个牛市周期开始买入，投资者仍然可以获得38倍回报。比特币从一开始就不会亏待相信它的人。 2.第二次牛市时间段：2011年10月至2013年4月，持续时间7个月，比特币产量减半时间2012年。比特币从2美元涨到260美元，上涨了130倍。随后不到一周的时间，比特币跌至50美元 ，跌幅80%。 3.第三次牛市时间段：2013年4月至2013年12月，持续时间8个月。比特币从50美元开始上涨，到2013年12月达到1160美元，涨了23倍。之后，一直跌到了2015年1月的150美元，下跌87%。但是，虽然比特币跌到150美元，在这个牛市启动开始时买入可以获得2倍回报。 4.第四次牛市时间段：2016年1月至2017年12月，持续时间2年，2016年7月比特币产量减半。比特币从2016年1月的400美元，开启缓慢上涨，到2017年12月涨到2万美元，涨了50倍。2017年，比特币从1...]]></description>
            <content:encoded><![CDATA[<p>1.第一次牛市时间段： 2010年9月---2011年6月，持续时间9个月。比特币从0.06美元涨到36美元，随后马上跌到2.5美元，跌幅达到93%，不少人说，比特币会死，但以后没有低于2美元。这意味着，在这个牛市周期开始买入，投资者仍然可以获得38倍回报。比特币从一开始就不会亏待相信它的人。</p><p>2.第二次牛市时间段：2011年10月至2013年4月，持续时间7个月，比特币产量减半时间2012年。比特币从2美元涨到260美元，上涨了130倍。随后不到一周的时间，比特币跌至50美元 ，跌幅80%。</p><p>3.第三次牛市时间段：2013年4月至2013年12月，持续时间8个月。比特币从50美元开始上涨，到2013年12月达到1160美元，涨了23倍。之后，一直跌到了2015年1月的150美元，下跌87%。但是，虽然比特币跌到150美元，在这个牛市启动开始时买入可以获得2倍回报。</p><p>4.第四次牛市时间段：2016年1月至2017年12月，持续时间2年，2016年7月比特币产量减半。比特币从2016年1月的400美元，开启缓慢上涨，到2017年12月涨到2万美元，涨了50倍。2017年，比特币从1月份的1000美元涨到5000美元，用了10个月;而从7000美元飙升到20000美元，只用了20天，大牛行情往往很短暂。2017年12月10日，芝加哥期权交易所推出比特币期货交易，比特币价格应声暴跌3000美元。随后，比特币价格又上升到了新高度，在20000美元关口徘徊，价格反复震荡，日涨跌幅度达数千美元。2018年1月6日，比特币突然从悬崖坠落，此后再也没有回到过1万美元的关口，2018年2月6日更是跌到5800美元，跌幅度70%。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">四次比特币牛市的共同特点是什么?</h2><p>1、每一次牛市初期投资比特币，至少可以获得20倍的投资回报，熊市和牛市初期都是适合建仓的时期。</p><p>2、比特币减半前一年或者一年半就会缓慢启动牛市，减半后一年多迎来大牛行情。</p><p>3、大牛市的爆发阶段，比特币被暴拉，持续时间一个月左右，比特币到顶后，一般会急跌，几天时间跌幅达60%以上。</p><p>4、每一个牛市过后，在接下来的熊市，比特币都会缩水80%以上，但每次都倔强的站起来了，每一次都是王者归来，吸引更多的韭菜入场，带来更大的牛市。</p><p>比特币几次牛市？通过以上介绍，相信大家对于比特币历史牛市时间有所了解，投资者不妨仔细回想投资历程就不难发现，最重要的是如何把握出场点位，而不是纠结何时去进场，只要掌握好的出场点位，做投资最后结果都能够盈利出局。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[使用python进行套利监控2]]></title>
            <link>https://paragraph.com/@java-3/python-2</link>
            <guid>U2EUC5IiKsdyQ4OQCPgb</guid>
            <pubDate>Wed, 30 Nov 2022 06:53:10 GMT</pubDate>
            <description><![CDATA[熟悉了python并踩过坑之后，觉得python还是挺好的。毕竟男人嘛，喜新厌旧都是可以理解的。emmmm……… 接上一节。 现在开始改写。毕竟一个dex市场是不够的。需要多个。 所以，我们要求有一个集合中间两个市场一直无法执行成功。估计是墙的问题。 FACTORY_ADDRESSES = [ CRO_FACTORY_ADDRESS, # ZEUS_FACTORY_ADDRESS, # LUA_FACTORY_ADDRESS, SUSHISWAP_FACTORY_ADDRESS, UNISWAP_FACTORY_ADDRESS, ] 循环的pagesize和最大页数 BATCH_COUNT_LIMIT = 10 UNISWAP_BATCH_SIZE = 50 修改queryPairs()def queryPairs(): pairList = [] for i in range(0, len(FACTORY_ADDRESSES)): factoryAddress = FACTORY_ADDRESSES[i] print("开始查询--->" + factoryAddress) r...]]></description>
            <content:encoded><![CDATA[<p>熟悉了python并踩过坑之后，觉得python还是挺好的。毕竟男人嘛，喜新厌旧都是可以理解的。emmmm………</p><p>接上一节。</p><p>现在开始改写。毕竟一个dex市场是不够的。需要多个。</p><p>所以，我们要求有一个集合</p><pre data-type="codeBlock" text="中间两个市场一直无法执行成功。估计是墙的问题。
FACTORY_ADDRESSES = [
    CRO_FACTORY_ADDRESS,
    # ZEUS_FACTORY_ADDRESS,
    # LUA_FACTORY_ADDRESS,
    SUSHISWAP_FACTORY_ADDRESS,
    UNISWAP_FACTORY_ADDRESS,
]
"><code>中间两个市场一直无法执行成功。估计是墙的问题。
<span class="hljs-attr">FACTORY_ADDRESSES</span> = [
    CRO_FACTORY_ADDRESS,
    <span class="hljs-comment"># ZEUS_FACTORY_ADDRESS,</span>
    <span class="hljs-comment"># LUA_FACTORY_ADDRESS,</span>
    SUSHISWAP_FACTORY_ADDRESS,
    UNISWAP_FACTORY_ADDRESS,
]
</code></pre><pre data-type="codeBlock" text="循环的pagesize和最大页数
BATCH_COUNT_LIMIT = 10
UNISWAP_BATCH_SIZE = 50
"><code>循环的pagesize和最大页数
<span class="hljs-attr">BATCH_COUNT_LIMIT</span> = <span class="hljs-number">10</span>
<span class="hljs-attr">UNISWAP_BATCH_SIZE</span> = <span class="hljs-number">50</span>
</code></pre><p>修改queryPairs()</p><pre data-type="codeBlock" text="def queryPairs():
    pairList = []
    for i in range(0, len(FACTORY_ADDRESSES)):
        factoryAddress = FACTORY_ADDRESSES[i]
        print(&quot;开始查询---&gt;&quot; + factoryAddress)
        result = uniswapQuery.functions.getPairsByIndexRange(factoryAddress, i, UNISWAP_BATCH_SIZE).call()
        for pair in result:
            pairList.append(EthMarketPair(pair[2], pair[0], pair[1]))

        if len(result) &lt; UNISWAP_BATCH_SIZE:
            continue
        # 如果需要进行token黑名单的校验，则需要获取下列的token地址，再进行类似!blacklistTokens.includes(tokenAddress)校验
        # if pair[0] == WETH_ADDRESS:
        #     tokenAddress = pair[1]
        # elif pair[1] == WETH_ADDRESS:
        #     tokenAddress = pair[0]
        # else:
        #     continue
    return pairList
"><code>def queryPairs():
    pairList <span class="hljs-operator">=</span> []
    <span class="hljs-keyword">for</span> i in range(<span class="hljs-number">0</span>, len(FACTORY_ADDRESSES)):
        factoryAddress <span class="hljs-operator">=</span> FACTORY_ADDRESSES[i]
        print(<span class="hljs-string">"开始查询--->"</span> <span class="hljs-operator">+</span> factoryAddress)
        result <span class="hljs-operator">=</span> uniswapQuery.functions.getPairsByIndexRange(factoryAddress, i, UNISWAP_BATCH_SIZE).<span class="hljs-built_in">call</span>()
        <span class="hljs-keyword">for</span> pair in result:
            pairList.append(EthMarketPair(pair[<span class="hljs-number">2</span>], pair[<span class="hljs-number">0</span>], pair[<span class="hljs-number">1</span>]))

        <span class="hljs-keyword">if</span> len(result) <span class="hljs-operator">&#x3C;</span> UNISWAP_BATCH_SIZE:
            <span class="hljs-keyword">continue</span>
        # 如果需要进行token黑名单的校验，则需要获取下列的token地址，再进行类似<span class="hljs-operator">!</span>blacklistTokens.includes(tokenAddress)校验
        # <span class="hljs-keyword">if</span> pair[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> WETH_ADDRESS:
        #     tokenAddress <span class="hljs-operator">=</span> pair[<span class="hljs-number">1</span>]
        # elif pair[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> WETH_ADDRESS:
        #     tokenAddress <span class="hljs-operator">=</span> pair[<span class="hljs-number">0</span>]
        # <span class="hljs-keyword">else</span>:
        #     <span class="hljs-keyword">continue</span>
    <span class="hljs-keyword">return</span> pairList
</code></pre><p>创建一个查询交易对的函数queryReservesByPairs</p><pre data-type="codeBlock" text="def queryReservesByPairs(marketAddresList):
    # 返回交易对的储备值, token0 --&gt;reserve[0], token1 --&gt;reserve[1], 时间戳reserve[2]
    result = uniswapQuery.functions.getReservesByPairs(marketAddresList).call()
    print(&quot;查询获取库存结果:&quot;)
    print(result[0])
    return result[0]
"><code>def queryReservesByPairs(marketAddresList):
    # 返回交易对的储备值, token0 <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">></span>reserve[<span class="hljs-number">0</span>], token1 <span class="hljs-operator">-</span><span class="hljs-operator">-</span><span class="hljs-operator">></span>reserve[<span class="hljs-number">1</span>], 时间戳reserve[<span class="hljs-number">2</span>]
    result <span class="hljs-operator">=</span> uniswapQuery.functions.getReservesByPairs(marketAddresList).<span class="hljs-built_in">call</span>()
    print(<span class="hljs-string">"查询获取库存结果:"</span>)
    print(result[<span class="hljs-number">0</span>])
    <span class="hljs-keyword">return</span> result[<span class="hljs-number">0</span>]
</code></pre><p>这里，先暂时进行打印输出，以便我们观察结果。</p><p>接下来就是修改main函数。</p><pre data-type="codeBlock" text="if __name__ == &apos;__main__&apos;:
    pairList = queryPairs()
    print(&quot;查询交易对结果集长度:%d&quot; % len(pairList))

    marketAddressList = list(map(lambda pair: pair.marketAddress, pairList))
    print(&quot;开始查询交易对储备%d&quot; % +len(marketAddressList))
    print(marketAddressList)
    queryReservesByPairs(marketAddressList)
"><code><span class="hljs-keyword">if</span> __name__ <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'__main__'</span>:
    pairList <span class="hljs-operator">=</span> queryPairs()
    print(<span class="hljs-string">"查询交易对结果集长度:%d"</span> <span class="hljs-operator">%</span> len(pairList))

    marketAddressList <span class="hljs-operator">=</span> list(map(lambda pair: pair.marketAddress, pairList))
    print(<span class="hljs-string">"开始查询交易对储备%d"</span> <span class="hljs-operator">%</span> <span class="hljs-operator">+</span>len(marketAddressList))
    print(marketAddressList)
    queryReservesByPairs(marketAddressList)
</code></pre><p>OK,如果顺利，会有以下提示</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a7f2aa73f626e155e325e5a61f0ff7a3f2069ce266c3d0678b3aa36430bfe363.png" alt="交易对返回的数据结构" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">交易对返回的数据结构</figcaption></figure><p>好了。坐稳了！下一节我们准备要上国道，之后再进收收费站上高速！！！</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[使用python监控套利机会1]]></title>
            <link>https://paragraph.com/@java-3/python-1</link>
            <guid>cvZ3bpdoBsvvfMEpbWYG</guid>
            <pubDate>Tue, 29 Nov 2022 06:51:29 GMT</pubDate>
            <description><![CDATA[近期使用JS来写监控套利，发现越写越坑。不得已把目光转向python。 再进行三分钟入门，一小时精通的技能后，下载PyCharm Community直接开搞。 在克服了定义函数没有大括号这种强迫症之后，错误才慢慢减少。 对了，还有for循环。。。 废话说完，现在开始开搞。 首先确保已经有了能够正常运行python的环境下。 安装web3如果你不想安装在系统环境里面，记得需要创建并激活虚拟环境，然后再安装web3. 打开PyCharm Community 新建项目。 新建一个类EthMarketPair.py，用于保存市场地址和token信息。代码如下：class EthMarketPair: def __init__(self, marketAddress, token0, token1): self.marketAddress = marketAddress self.tokens = [token0, token1] self.token0 = token0 self.token1 = token1 marketAddress = "" tokens = [","] toke...]]></description>
            <content:encoded><![CDATA[<p>近期使用JS来写监控套利，发现越写越坑。不得已把目光转向python。</p><p>再进行三分钟入门，一小时精通的技能后，下载PyCharm Community直接开搞。</p><p>在克服了定义函数没有大括号这种强迫症之后，错误才慢慢减少。</p><p>对了，还有for循环。。。</p><p>废话说完，现在开始开搞。</p><p>首先确保已经有了能够正常运行python的环境下。</p><p>安装web3</p><pre data-type="codeBlock" text="pip install web3
"><code></code></pre><p>如果你不想安装在系统环境里面，记得需要创建并激活虚拟环境，然后再安装web3.</p><p>打开PyCharm Community 新建项目。</p><p>新建一个类EthMarketPair.py，用于保存市场地址和token信息。代码如下：</p><pre data-type="codeBlock" text="class EthMarketPair:
    def __init__(self, marketAddress, token0, token1):
        self.marketAddress = marketAddress
        self.tokens = [token0, token1]
        self.token0 = token0
        self.token1 = token1


    marketAddress = &quot;&quot;
    tokens = [&quot;,&quot;]
    token0 = &quot;&quot;
    token1 = &quot;&quot;
"><code>class EthMarketPair:
    def __init__(<span class="hljs-built_in">self</span>, marketAddress, token0, token1):
        <span class="hljs-built_in">self</span>.marketAddress <span class="hljs-operator">=</span> marketAddress
        <span class="hljs-built_in">self</span>.tokens <span class="hljs-operator">=</span> [token0, token1]
        <span class="hljs-built_in">self</span>.token0 <span class="hljs-operator">=</span> token0
        <span class="hljs-built_in">self</span>.token1 <span class="hljs-operator">=</span> token1


    marketAddress <span class="hljs-operator">=</span> <span class="hljs-string">""</span>
    tokens <span class="hljs-operator">=</span> [<span class="hljs-string">","</span>]
    token0 <span class="hljs-operator">=</span> <span class="hljs-string">""</span>
    token1 <span class="hljs-operator">=</span> <span class="hljs-string">""</span>
</code></pre><p>再新建另一个py文件EthMarkets.py，以后的操作将会在这里面进行。</p><p>由于现在只进行简单操作。只进行一个市场的信息查询。所以，预先定义一些常量值</p><pre data-type="codeBlock" text="WETH_ADDRESS = &apos;0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2&apos;

#uniswap的查询地址
UNISWAP_LOOKUP_CONTRACT_ADDRESS = &apos;0x5EF1009b9FCD4fec3094a5564047e190D72Bd511&apos;

#crypto的合约地址
CRO_FACTORY_ADDRESS = &quot;0x9DEB29c9a4c7A88a3C0257393b7f3335338D9A9D&quot;
"><code><span class="hljs-attr">WETH_ADDRESS</span> = <span class="hljs-string">'0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'</span>

<span class="hljs-comment">#uniswap的查询地址</span>
<span class="hljs-attr">UNISWAP_LOOKUP_CONTRACT_ADDRESS</span> = <span class="hljs-string">'0x5EF1009b9FCD4fec3094a5564047e190D72Bd511'</span>

<span class="hljs-comment">#crypto的合约地址</span>
<span class="hljs-attr">CRO_FACTORY_ADDRESS</span> = <span class="hljs-string">"0x9DEB29c9a4c7A88a3C0257393b7f3335338D9A9D"</span>
</code></pre><p>定义连接web3的RPC</p><pre data-type="codeBlock" text="#记得顶部需要import
#还要记得需要启动ganache-cli
w3 = Web3(Web3.HTTPProvider(&apos;http://127.0.0.1:8545&apos;))
uniswapQuery = w3.eth.contract(address=UNISWAP_LOOKUP_CONTRACT_ADDRESS, abi=UNISWAP_QUERY_ABI)
"><code>#记得顶部需要<span class="hljs-keyword">import</span>
#还要记得需要启动<span class="hljs-title">ganache</span><span class="hljs-operator">-</span><span class="hljs-title">cli</span>
<span class="hljs-title">w3</span> <span class="hljs-operator">=</span> <span class="hljs-title">Web3</span>(<span class="hljs-title">Web3</span>.<span class="hljs-title">HTTPProvider</span>(<span class="hljs-string">'http://127.0.0.1:8545'</span>))
<span class="hljs-title">uniswapQuery</span> <span class="hljs-operator">=</span> <span class="hljs-title">w3</span>.<span class="hljs-title">eth</span>.<span class="hljs-title"><span class="hljs-keyword">contract</span></span>(<span class="hljs-title"><span class="hljs-keyword">address</span></span><span class="hljs-operator">=</span><span class="hljs-title">UNISWAP_LOOKUP_CONTRACT_ADDRESS</span>, <span class="hljs-title"><span class="hljs-built_in">abi</span></span><span class="hljs-operator">=</span><span class="hljs-title">UNISWAP_QUERY_ABI</span>)
</code></pre><p>定义查询函数</p><pre data-type="codeBlock" text="def queryPairs():
    factoryAddress = CRO_FACTORY_ADDRESS 
    result = uniswapQuery.functions.getPairsByIndexRange(factoryAddress, 0, 1000).call()
    pairList = []
    for pair in result:
        # 如果需要进行token黑名单的校验，则需要获取下列的token地址，再进行类似!blacklistTokens.includes(tokenAddress)校验
        # if pair[0] == WETH_ADDRESS:
        #     tokenAddress = pair[1]
        # elif pair[1] == WETH_ADDRESS:
        #     tokenAddress = pair[0]
        # else:
        #     continue
        pairList.append(EthMarketPair(pair[2], pair[0], pair[1]))
    return pairList
"><code>def queryPairs():
    factoryAddress <span class="hljs-operator">=</span> CRO_FACTORY_ADDRESS 
    result <span class="hljs-operator">=</span> uniswapQuery.functions.getPairsByIndexRange(factoryAddress, <span class="hljs-number">0</span>, <span class="hljs-number">1000</span>).<span class="hljs-built_in">call</span>()
    pairList <span class="hljs-operator">=</span> []
    <span class="hljs-keyword">for</span> pair in result:
        # 如果需要进行token黑名单的校验，则需要获取下列的token地址，再进行类似<span class="hljs-operator">!</span>blacklistTokens.includes(tokenAddress)校验
        # <span class="hljs-keyword">if</span> pair[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> WETH_ADDRESS:
        #     tokenAddress <span class="hljs-operator">=</span> pair[<span class="hljs-number">1</span>]
        # elif pair[<span class="hljs-number">1</span>] <span class="hljs-operator">=</span><span class="hljs-operator">=</span> WETH_ADDRESS:
        #     tokenAddress <span class="hljs-operator">=</span> pair[<span class="hljs-number">0</span>]
        # <span class="hljs-keyword">else</span>:
        #     <span class="hljs-keyword">continue</span>
        pairList.append(EthMarketPair(pair[<span class="hljs-number">2</span>], pair[<span class="hljs-number">0</span>], pair[<span class="hljs-number">1</span>]))
    <span class="hljs-keyword">return</span> pairList
</code></pre><p>编写main函数，打印返回长度</p><pre data-type="codeBlock" text="if __name__ == &apos;__main__&apos;:
    pairList = queryPairs()
    print(&quot;长度:%d&quot; % len(pairList))
"><code><span class="hljs-keyword">if</span> __name__ <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'__main__'</span>:
    pairList <span class="hljs-operator">=</span> queryPairs()
    print(<span class="hljs-string">"长度:%d"</span> <span class="hljs-operator">%</span> len(pairList))
</code></pre><p>记得千万别写print(&quot;长度：&quot; + len(pairList))，会报错的。别问我怎么知道。。。。</p><p>OK，运行程序！程序返回ETH交易对的结果集。</p><pre data-type="codeBlock" text="长度:99
"><code><span class="hljs-section">长度:99</span>
</code></pre><p>好了。走完这一个之后，下面再根据地址去查询储备信息。之后改进一下，可以进行批量查询。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[ethers.js一些常用知识]]></title>
            <link>https://paragraph.com/@java-3/ethers-js</link>
            <guid>2n7uQCWgRY6pt4sSCFmn</guid>
            <pubDate>Sun, 20 Nov 2022 09:13:18 GMT</pubDate>
            <description><![CDATA[Provider（在ethers中）是一个为以太坊网络连接提供抽象的类。它提供对区块链及其状态的只读访问。签名者是一个（通常）以某种方式直接或间接访问私钥的类，它可以签署消息和交易以授权网络向您的帐户收取以太币，执行操作。合约是一种抽象，表示与以太坊网络上特定合约的连接，因此应用程序可以像使用普通 JavaScript 对象一样使用它。工具包提供了大量的通用实用函数去编写 dapps、处理用户输入和格式化数据等功能。比如说发送交易：const tx = signer.sendTransaction({//签名，发送交易 to: "ricmoo.firefly.eth",//发送至以太坊域名 value: ethers.utils.parseEther("1.0")//格式化 });]]></description>
            <content:encoded><![CDATA[<ul><li><p>Provider（在ethers中）是一个为以太坊网络连接提供抽象的类。它提供对区块链及其状态的只读访问。</p></li><li><p>签名者是一个（通常）以某种方式直接或间接访问私钥的类，它可以签署消息和交易以授权网络向您的帐户收取以太币，执行操作。</p></li><li><p>合约是一种抽象，表示与以太坊网络上特定合约的连接，因此应用程序可以像使用普通 JavaScript 对象一样使用它。</p></li><li><p>工具包提供了大量的通用实用函数去编写 dapps、处理用户输入和格式化数据等功能。</p></li></ul><p>比如说发送交易：</p><pre data-type="codeBlock" text="const tx = signer.sendTransaction({//签名，发送交易
    to: &quot;ricmoo.firefly.eth&quot;,//发送至以太坊域名
    value: ethers.utils.parseEther(&quot;1.0&quot;)//格式化
});
"><code>const <span class="hljs-built_in">tx</span> <span class="hljs-operator">=</span> signer.sendTransaction({<span class="hljs-comment">//签名，发送交易</span>
    to: <span class="hljs-string">"ricmoo.firefly.eth"</span>,<span class="hljs-comment">//发送至以太坊域名</span>
    <span class="hljs-built_in">value</span>: ethers.utils.parseEther(<span class="hljs-string">"1.0"</span>)<span class="hljs-comment">//格式化</span>
});
</code></pre>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[使用flashbots监控套利机会]]></title>
            <link>https://paragraph.com/@java-3/flashbots</link>
            <guid>9cD87KyNPel4iDPnwd9A</guid>
            <pubDate>Fri, 18 Nov 2022 09:15:50 GMT</pubDate>
            <description><![CDATA[最近又又又经历了一看就会，一做就废的痛苦经历。看着文档：非常简单，不过如此！！！ 真正走一遍流程的时候：卧槽。怎么回事啊？ 尤其是看defi的文档。真心脑壳疼。 所以，再次告诉自己，你必须要自己动手一遍，你才能够知道，你到底有没有理解和掌握它]]></description>
            <content:encoded><![CDATA[<p>最近又又又经历了一看就会，一做就废的痛苦经历。看着文档：非常简单，不过如此！！！</p><p>真正走一遍流程的时候：卧槽。怎么回事啊？</p><p>尤其是看defi的文档。真心脑壳疼。</p><p>所以，再次告诉自己，你必须要自己动手一遍，你才能够知道，你到底有没有理解和掌握它</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[清算机器人，从0到1跑通全流程]]></title>
            <link>https://paragraph.com/@java-3/0-1</link>
            <guid>rt9vBw8y09bxdavHVHxZ</guid>
            <pubDate>Mon, 14 Nov 2022 11:56:20 GMT</pubDate>
            <description><![CDATA[本文翻译自Robert Miller的Anatomy of an MEV Strategy: Synthetix 几个月前，臭名昭著的阿尔法泄密者KALEB在Flashbots公共搜索者不和谐中，发布了以下消息：KALEB此前曾泄露了数十万美元的Alpha版本，用于对Synthetix的更改。 在这个机器人操作者的巢穴里分享阿尔法就像向狮子扔红肉一样，快速浏览一下合同，就可以确认这是一笔令人眼花缭乱的资金。 在接下来的几周里，我计划并试图执行一项策略来捕获上面分享的MEV KALEB。第一步，识别机会我不是一个Synthetix专家，因此第一步是去学习我将要涉及的操作。具体如下：我找出了相关合约我在Synthetix博客里读了它们的高级别功能并且搜索了相关文档我确保理解了将要实行的治理变动范围我查找了相关函数总结一下这阶段的工作，Synthetix已经试验了以ETH为抵押去铸造(mint)sUSD和sETH。你可以在合约中存入ETH并铸造那些资产，只要你留意你的抵押资产不要下跌到低于你铸造的资产的一定水平。(可能亏钱) 然后，在一年后，协议投票通过结束了此试验。当还有数百万差不多...]]></description>
            <content:encoded><![CDATA[<p><strong>本文翻译自<em>Robert Miller</em>的</strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.bertcmiller.com/2021/09/05/mev-synthetix.html"><strong>Anatomy of an MEV Strategy: Synthetix</strong></a></p><p>几个月前，臭名昭著的阿尔法泄密者KALEB在Flashbots公共搜索者不和谐中，发布了以下消息：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/50f4906dad88cde49a6531e581131e8640628349b89d7adebf3e25d8453a4406.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>KALEB此前曾泄露了数十万美元的Alpha版本，用于对Synthetix的更改。</p><p>在这个机器人操作者的巢穴里分享阿尔法就像向狮子扔红肉一样，快速浏览一下合同，就可以确认这是一笔令人眼花缭乱的资金。</p><p>在接下来的几周里，我计划并试图执行一项策略来捕获上面分享的MEV KALEB。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">第一步，识别机会</h3><p>我不是一个Synthetix专家，因此第一步是去学习我将要涉及的操作。具体如下：</p><ul><li><p>我找出了相关合约</p></li><li><p>我在Synthetix博客里读了它们的高级别功能并且搜索了相关文档</p></li><li><p>我确保理解了将要实行的治理变动范围</p></li><li><p>我查找了相关函数</p></li></ul><p>总结一下这阶段的工作，Synthetix已经试验了以ETH为抵押去铸造(mint)sUSD和sETH。你可以在合约中存入ETH并铸造那些资产，只要你留意你的抵押资产不要下跌到低于你铸造的资产的一定水平。(可能亏钱)</p><p>然后，在一年后，协议投票通过结束了此试验。当还有数百万差不多未偿还时他们要怎么处理呢？很好，你将可以清算任何头寸。</p><p>在一段很长的警告期后，贷款将在一个区块内从安全变为可让任何人清算，不管任何金额的抵押物。这将在公共的mempool里被由pDAO地址发出的一笔交易而触发。</p><p>为了清算贷款，我需要偿还我已借的未还金额(sUSD或者sETH)。做为回报，我将收到我关闭的贷款的抵押物ETH。做为清算激励，我将得到比我偿还的sETH或者sUSD更高价值的抵押物。当前还有数百分美金仍然处于贷款状态，这意味着有一大笔钱可以让清算者来赚。因此，我将会尾追pDAO的交易，尽最大可能性让我从这个机会中获利。</p><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">第 2 步：了解机会</h3><p>现在，我知道了基础的机制，并且找到了一些相关的函数。我继续深入研究哪些函数是我要调用到的，哪些数据是我需要的，以及如何生成这些数据。</p><p>以下是两个要在生产环境调用的函数：</p><ul><li><p>setLoanLiquidationOpen() 只能被合约所有者(pDAO)调用。允许清算未关闭的贷款。</p></li><li><p>liquidateUnclosedLoan() 传入贷款ID和帐户地址并清算该贷款。在setLoanLiquidationOpen()被调用之后，任何人都可以调用此方法。</p></li></ul><p>请注意，还有其他的，但我很快发现它们并不相关。现在我需要弄清楚我将如何选择要清算的贷款以及我需要多少sETH/sUSD。下列函数是我可以开始调用的：</p><ul><li><p>openLoanIDsByAccount():返回某地址相关的未关闭贷款ID</p></li><li><p>getLoanInformation():传入ID和地址返回贷款数据</p></li></ul><p>然而，有两个问题。让我感到困惑，这些合同不会告诉你哪些地址有未偿还的贷款。我在几分钟内找到了解决方法，通过在Etherscan上下载与合约相关的所有交易，并使用Excel创建与它们交互的唯一帐户列表。</p><ul><li><p>用getLoans去找到此地址是否有未还贷款，如果有就记录下贷款ID</p></li><li><p>用getLoanInformation去获取抵押物数量，铸造了多少uUSD/sETH，累积多少利息</p></li><li><p>按贷款金额从大到小排序并保存到json文件里</p></li></ul><p>第二个难点是并不能马上清楚的知道在清算某个贷款后我可以获得多少抵押物。因为有一个未还贷款的函数可以粗略的估算，但我需要更精确一些。因此，我研究了清算的代码并了解了相关数字是如何生成的。</p><p>以上是我如何获取链上数据并计算的过程。但是这么做很消耗gas。考虑到我将要与其它机器人在合约gas交易上竞争，实行链下计算，最小化gas消耗对我来说很重要。</p><p>经过几次迭代之后，我了解了需要从链上获取的最小数量的数据，是一些关于贷款的变量，我可以在链下处理完成后传入我的合约。然而，这些获取信息的函数太复杂了，查询所有的贷款信息会耗费超过一个区块的时间。这不能够接受。为解决这些问题，我写了一个简短的合约去批量获取数据，这提高了超过10倍的效率。核心代码：</p><pre data-type="codeBlock" text="function batchGetLoanInformation(address[] calldata _addresses, uint256[] calldata _loanIDs, address _contractAddress) external view returns ( 
        uint256[] memory, 
        uint256[] memory){

        uint256[] memory totalRepayment = new uint256Unsupported embed;
        uint256[] memory totalCollateralLiquidated = new uint256Unsupported embed;

        for (uint i = 0; i &lt; _addresses.length; i++){
            uint loanAmount;
            uint accruedInterest;

            (,, 
            loanAmount,,,, 
            accruedInterest,
            ) = collateralContract(_contractAddress).getLoan(_addresses[i], _loanIDs[i]);
            
            totalRepayment[i] = loanAmount + accruedInterest;

            totalCollateralLiquidated[i] = getCollateralAmountSUSD(sUSD, totalRepayment[i], COLLATERAL);
        }
        return (totalRepayment, totalCollateralLiquidated);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">batchGetLoanInformation</span>(<span class="hljs-params"><span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> _addresses, <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">calldata</span> _loanIDs, <span class="hljs-keyword">address</span> _contractAddress</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"> 
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span>, 
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span></span>)</span>{

        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> totalRepayment <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> uint256Unsupported embed;
        <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> totalCollateralLiquidated <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> uint256Unsupported embed;

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> _addresses.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>){
            <span class="hljs-keyword">uint</span> loanAmount;
            <span class="hljs-keyword">uint</span> accruedInterest;

            (,, 
            loanAmount,,,, 
            accruedInterest,
            ) <span class="hljs-operator">=</span> collateralContract(_contractAddress).getLoan(_addresses[i], _loanIDs[i]);
            
            totalRepayment[i] <span class="hljs-operator">=</span> loanAmount <span class="hljs-operator">+</span> accruedInterest;

            totalCollateralLiquidated[i] <span class="hljs-operator">=</span> getCollateralAmountSUSD(sUSD, totalRepayment[i], COLLATERAL);
        }
        <span class="hljs-keyword">return</span> (totalRepayment, totalCollateralLiquidated);
    }
</code></pre><p>现在我有一个快速的方式告诉你我需要多少sETH/sUSD，我可以获得多少抵押物，有多少利润。</p><p>总结：这阶段的工作是深入理解机会并用高效的方式收集需要的数据。你需要尽量在链下操作去减小gas消耗。成果是<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/1e37690638bf42e849b87a25b468eae20e65545d/execute/monitor-sUSD.js">两个</a>快速的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/1e37690638bf42e849b87a25b468eae20e65545d/execute/monitor-sETH.js">脚本</a>去获取我需要的数据。</p><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">步骤 3：创建执行合同</h3><p>你可能需要一个专门的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/tree/main/hardhat/contracts">合约</a>来提取MEV。我写了一个合约并在<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/tree/main/hardhat">测试环境</a>中测试以更好的理解合约并确保的我数据是正确的。这跟第2步和第4步是同步进行的。</p><p>闪电贷可以解决我数百万美金的sUSD/uETH资金需求。经过一番思考，我意识到无论如何我都需要把ETH换成其它资产，但我要选择在清算前还是清算后去做这个动作。</p><ul><li><p>选择1：Flashloan ETH -&gt; swap for USDC -&gt; swap for sUSD -&gt; liquidate sUSD loan -&gt; receive ETH -&gt; pay back ETH flashloan</p></li><li><p>选择2：Flashloan sUSD -&gt; liquidate sUSD loan -&gt; receive ETH -&gt; swap for USDC -&gt; swap for sUSD -&gt; pay back sUSD flashloan</p></li></ul><p>借出sUSD只有Aave支持，而ETH可以在很多闪电贷提供商那里借出，最终就是选择哪家闪电贷提供商的问题。最终，我选择了方式1，因为dYdX没有手续费，而Aave有手续费。</p><p>dYdX会加2wei的费用，相当于免费；Aave闪电贷需要0.09%的手续费（franx.eth的解释）</p><h3 id="h-gas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Gas优化</h3><p>你可以在这里找到<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/contracts/dYdXLiquidator.sol">完整的合约</a>，下面是从dYdX借到WETH后处理清算sUSD贷款的一段代码：</p><pre data-type="codeBlock" text="// This is the function called by dydx after giving us the loan
    function callFunction(address sender, Account.Info memory accountInfo, bytes memory data) external {
        // Use chi tokens 
        uint256 gasStart = gasleft();

        // Let the executor or the dYdX contract call this function
        // probably fine to restrict to dYdX
        require(msg.sender == executor || msg.sender == address(soloMargin));
        
        // Decode the passed variables from the data object
        (
            address[] memory sUSDAddresses,
            uint256[] memory sUSDLoanIDs,
            uint256 wethEstimate,
            uint256 usdcEstimate,
            uint256 ethToCoinbase
        ) 
            = abi.decode(data, 
        (
            address[],
            uint256[],
            uint256,
            uint256,
            uint256
        ));

        // Swap WETH for USDC on uniswap v3
        uniswapRouter.exactOutputSingle(
            ISwapRouter.ExactOutputSingleParams(
                address(WETH),        // address tokenIn;
                usdcTokenAddress,     // address tokenOut;
                3000,                 // uint24 fee;
                address(this),        // address recipient;
                10**18,               // uint256 deadline;
                usdcEstimate,         // uint256 amountOut;
                wethEstimate,         // uint256 amountInMaximum;
                0                     // uint160 sqrtPriceLimitX96;
            )
        );

        // Swap USDC for sUSD on Curve
        curvePoolSUSD.exchange_underlying(
            1, // usdc
            3, // sUSD
            usdcEstimate, // usdc input
            1); // min sUSD, generally not advisible to make a trade with a min amount out of 1, but its fine here I think because the overall risk of getting rekt is low
        
        // Liquidate the loans
        for (uint256 i = 0; i &lt; sUSDAddresses.length; i++) {
            sUSDLoansAddress.liquidateUnclosedLoan(sUSDAddresses[i], sUSDLoanIDs[i]);
        }

        // We got back ETH but must pay dYdX in WETH, so deposit our whole balance sans what is paid to miners
        WETH.deposit{value: address(this).balance - ethToCoinbase}();

        // Pay the miner
        block.coinbase.transfer(ethToCoinbase);

        // Use for chi tokens
        uint256 gasSpent = 21000 + gasStart - gasleft() + (16 * msg.data.length);
        CHI.freeFromUpTo(owner, (gasSpent + 14154) / 41947);
    }
"><code><span class="hljs-comment">// This is the function called by dydx after giving us the loan</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callFunction</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> sender, Account.Info <span class="hljs-keyword">memory</span> accountInfo, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> </span>{
        <span class="hljs-comment">// Use chi tokens </span>
        <span class="hljs-keyword">uint256</span> gasStart <span class="hljs-operator">=</span> <span class="hljs-built_in">gasleft</span>();

        <span class="hljs-comment">// Let the executor or the dYdX contract call this function</span>
        <span class="hljs-comment">// probably fine to restrict to dYdX</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> executor <span class="hljs-operator">|</span><span class="hljs-operator">|</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(soloMargin));
        
        <span class="hljs-comment">// Decode the passed variables from the data object</span>
        (
            <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> sUSDAddresses,
            <span class="hljs-keyword">uint256</span>[] <span class="hljs-keyword">memory</span> sUSDLoanIDs,
            <span class="hljs-keyword">uint256</span> wethEstimate,
            <span class="hljs-keyword">uint256</span> usdcEstimate,
            <span class="hljs-keyword">uint256</span> ethToCoinbase
        ) 
            <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, 
        (
            <span class="hljs-keyword">address</span>[],
            <span class="hljs-keyword">uint256</span>[],
            <span class="hljs-keyword">uint256</span>,
            <span class="hljs-keyword">uint256</span>,
            <span class="hljs-keyword">uint256</span>
        ));

        <span class="hljs-comment">// Swap WETH for USDC on uniswap v3</span>
        uniswapRouter.exactOutputSingle(
            ISwapRouter.ExactOutputSingleParams(
                <span class="hljs-keyword">address</span>(WETH),        <span class="hljs-comment">// address tokenIn;</span>
                usdcTokenAddress,     <span class="hljs-comment">// address tokenOut;</span>
                <span class="hljs-number">3000</span>,                 <span class="hljs-comment">// uint24 fee;</span>
                <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>),        <span class="hljs-comment">// address recipient;</span>
                <span class="hljs-number">10</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">18</span>,               <span class="hljs-comment">// uint256 deadline;</span>
                usdcEstimate,         <span class="hljs-comment">// uint256 amountOut;</span>
                wethEstimate,         <span class="hljs-comment">// uint256 amountInMaximum;</span>
                <span class="hljs-number">0</span>                     <span class="hljs-comment">// uint160 sqrtPriceLimitX96;</span>
            )
        );

        <span class="hljs-comment">// Swap USDC for sUSD on Curve</span>
        curvePoolSUSD.exchange_underlying(
            <span class="hljs-number">1</span>, <span class="hljs-comment">// usdc</span>
            <span class="hljs-number">3</span>, <span class="hljs-comment">// sUSD</span>
            usdcEstimate, <span class="hljs-comment">// usdc input</span>
            <span class="hljs-number">1</span>); <span class="hljs-comment">// min sUSD, generally not advisible to make a trade with a min amount out of 1, but its fine here I think because the overall risk of getting rekt is low</span>
        
        <span class="hljs-comment">// Liquidate the loans</span>
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint256</span> i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&#x3C;</span> sUSDAddresses.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
            sUSDLoansAddress.liquidateUnclosedLoan(sUSDAddresses[i], sUSDLoanIDs[i]);
        }

        <span class="hljs-comment">// We got back ETH but must pay dYdX in WETH, so deposit our whole balance sans what is paid to miners</span>
        WETH.deposit{<span class="hljs-built_in">value</span>: <span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>).<span class="hljs-built_in">balance</span> <span class="hljs-operator">-</span> ethToCoinbase}();

        <span class="hljs-comment">// Pay the miner</span>
        <span class="hljs-built_in">block</span>.<span class="hljs-built_in">coinbase</span>.<span class="hljs-built_in">transfer</span>(ethToCoinbase);

        <span class="hljs-comment">// Use for chi tokens</span>
        <span class="hljs-keyword">uint256</span> gasSpent <span class="hljs-operator">=</span> <span class="hljs-number">21000</span> <span class="hljs-operator">+</span> gasStart <span class="hljs-operator">-</span> <span class="hljs-built_in">gasleft</span>() <span class="hljs-operator">+</span> (<span class="hljs-number">16</span> <span class="hljs-operator">*</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">data</span>.<span class="hljs-built_in">length</span>);
        CHI.freeFromUpTo(owner, (gasSpent <span class="hljs-operator">+</span> <span class="hljs-number">14154</span>) <span class="hljs-operator">/</span> <span class="hljs-number">41947</span>);
    }
</code></pre><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://raw.githubusercontent.com/bertmiller/sMEV/main/contracts/dYdXLiquidator.sol">https://raw.githubusercontent.com/bertmiller/sMEV/main/contracts/dYdXLiquidator.sol</a></p><p>我花了很多时间去最小化gas消耗。下面列出我的一些设计准则：</p><ul><li><p>替代发出多个单独的清算和交换交易，我选择将多个清算合并到一个交易，这样可以在清算中摊销固定gas费用，提高bundle的竞争力。</p></li><li><p>我需要寻找最佳的路径把ETH换成USDC再换成sUSD，需要决定在Uniswap v3里用exactInput还是exactOutput函数。不管如何，我都会遭遇一些滑点，所以我选择用<strong>exactOutput去避免调用balanceOf</strong>。</p></li><li><p>在交易精确性和gas交易上存在一些权衡。只要能成功偿还贷款，在精度上都会有些小缺点，因为我要在gas交易上进行竞争，我选择了它。</p></li></ul><p>需要注意的更多<em>战术</em>事项：</p><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/contracts/dYdXLiquidator.sol#L265-L268">在合约构造函数里前置授权所有</a>。这种方式，将在部署合约的时候支付gas费用，并降低在执行过程中的gas费。</p></li><li><p>在合约里燃烧gas token，而不是在我的地址里燃烧。</p><blockquote><p>作者用了chi gastoken做gas的回补，可以减小gas的消耗，又因为gas fee是一次性通过coinbase.transfer支付的，因此，gas总量越低，gas price就越高，对于miner来说越有吸引力。</p></blockquote></li><li><p>函数命名让<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solidity-by-example.org/function-selector/">selectors</a>以0s前导，这也可以降低一点gas消耗</p><blockquote><p>函数flashloan_4247(uint _loanAmount, bytes memory _params, uint8 _trigger)的selector是0x0000eaff。</p></blockquote></li><li><p>直接使用require比用modifier更省gas</p></li><li><p>还有一些可以被优化的点，比例：用gas fee代替coinbase.transfer</p></li><li><p>0xSisyphus慷慨的要提供ETH贷款给我让我代替闪电贷，可以节省一笔gas支出。然后在一些大笔贷款被偿还了之后，总体机会在减小。我决定不从Sisyphus那里借款，因为机会不再足够大到值得去这么做。</p></li></ul><p>总结：在这个阶段，我创建了智能合约去执行可用的MEV机会。要做到最佳需要努力思考正确的关于如何最小化gas消耗的策略。这个合约是迭代开发的，与数据工作同步进行，并且在测试环境中测试。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">第四步，计划执行</h3><h3 id="h-mevgas" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">清算MEV和优化gas费用的经济学</h3><p>有了精心编写的合约和对机会的深入理解，我需要去优化如何执行的策略。回顾了Flashbots MEV-Geth客户端高效执行拍卖时根据<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.flashbots.net/flashbots-auction/searchers/advanced/bundle-pricing">高gas price bundle赢</a>并上链的方式。最重要的因素是我需要最大化bundle的gas price，而不是总的支付了多少ETH。</p><p>考虑到这一点，我将之前收集到的数据整理到表格里去优化我的gas price。我的合约有固定的gas支出和可变的gas支出。固定的gas支出是闪电贷和交换。可变的gas支出是我想要清算多少个贷款。很直观的可以想到将有一些点到达清算的边际收益而不再值得去消耗gas。我运行了几次测试去获取实际的数字。</p><p>下面是我的结果：</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/05508a319d03d911824e281ab733ac9a61dd99e3ea49785cbb5122c862ab3c6e.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>结果有些惊喜，只清算30个sUSD贷款中的前4个是gas最高效的。在这之后的每个贷款都将增加总收益，但会降低bundle的gas price，让其减少竞争力。如果其它人试图一次性清算前10个sUSD贷款，那么他们将降低至少30%的gas效用！</p><p>考虑到未偿还的sETH贷款比较少，在一个交易里只清算sUSD而不是将sUSD和sETH合并清算会更有意义。因此潜在的奖励会比较少，也就支付更少的费用给矿工，意味着缺少gas竞争力。想到这我不禁笑起来。如果有人贪婪的一次性清算所有贷款，或者懒惰的一个一个头寸单独清算，那么我将胜出。</p><p>然而，其它贷款还在那里且有利润可供清算！再次，我试图去优化gas price，发现如果我清算前4个sUSD贷款再接着清算6个最大的sUSD贷款再接着2个sETH贷款，这样是gas最高效的。进而，假设我赢了，我还可以用前面bundle收益的ETH来代替闪电贷。</p><h3 id="h-flashbotsbundle" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Flashbots拍卖和我的bundle排序策略</h3><p>重复一遍：我在GAS效率方面竞争，但我也想通过清算每笔贷款来最大化我的利润。最佳战略是每一bundle提交一些清算，分别bundle。然后，这些将由闪电机器人拍卖独立进行评估。然后，这些将由Flashbots拍卖独立评估。但是，<em>每个</em>bundle<em>都依赖于 pDAO 的交易，使任何人都可以清算贷款。</em></p><p>如果pDAO交易没有在bundle里，那bundle将会失败。但是如果我包括的pDAO交易在每个bundle里，就只会有一个bundle成功。在一个bundle打包后其它bundle将不再有效，因此它们尝试去二次打包pDAO交易。因此，我需要一些方式只在第一个bundle里发送pDAO交易，但是确保其它bundle不会因为没有包括pDAO交易而失败和被抛弃。</p><p>解决方案是Flashbots拍卖的一个细微差别。在搜索者开始”<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://twitter.com/bertcmiller/status/1407305924600029189"><em>在bundle合并后降低矿工费用来戏弄拍卖</em></a><em>”</em>，Flashbots设置了两轮的模拟。</p><p>首先，所有bundle单独模拟以获取他们的gas price并检查失败。再来，<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/flashbots/mev-geth/blob/master/miner/worker.go#L1292-L1301">成功的bundle按gas price从大到小排序，再次模拟去找到冲突的bundle并确保没有bundle的gas price是低于期望的</a>。除非你试图去做些工作，否则你的bundle的gas price将不会在合并后降低。</p><p>意识到我可以做一些与上述搜索者相反的工作，来让我的bundle不会支付低于预期的矿工费，而是在第二轮的模拟中超付费。为了实现这个方案，我将要在第一个bundle里提交带pDAO交易，但是要额外检测剩余的bundle。这些bundle将会推测出他们在第几轮，并改变相关的执行。如果他们在第一轮他们将不会清算，因为如果他们想要清算将会失败，并不顾一切支付给矿工高额的gas price以通过第一轮的模拟。</p><p>我是如何检测我的bundle处于第几轮的呢？用检查合约余额的方式。如果在前面的bundle成功清算了，我的余额会因为利润而增加。因此，我增加了一个<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/contracts/dYdXLiquidator.sol#L128-L133">是否获得WETH利润的条件</a>检查，再处理清算。这在测试中通过了。</p><p>也就是说假设，一次提交三个bundle，第一个bundle带有pDAO交易，第二三个没有pDAO交易。在Flashbots在第一轮模拟时，正常情况下第二三个bundle会因为没有带pDAO而执行失败，然后会被抛弃，从而不会被打包入块。作者在合约里做了一个条件判断，用合约的WETH余额来判断是第几轮，因为第一轮是单独模拟，所以WETH余额肯定是0，还有另外一个trigger参数是用来区分是不是第一个bundle的，这样能判断出是第二三个bundle且是第一轮模拟，然后就强制用coinbase.transfer转gas给矿工，让交易不会失败且有gas费，让矿工愿意打包这个bundle。（译文作者注释）</p><p>总结：这个阶段还是关于策略。我用之前获取的数据、合约和测试环境来思考我要争夺的 MEV 机会的经济学逻辑，以及最优策略会是什么。用真实数据，我发现了一个令人惊讶的显性因子，但它很难执行。执行它需要一种新方式来提交bundle。</p><h2 id="h-5" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">第 5 步：执行</h2><p>有了数据，合约，计划，我就可以开始执行了。本质上，我需要编写能够执行我上述计划的bundle并且监控mempool里相关的Synthetix交易去尾追。这些大部分都是实现上的问题。 首先，我用<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/execute/builder.js#L40-L66">Blocknative去监控pDAO帐户的相关交易</a>。我把pDAO相关的所有交易都流向我的机器人。</p><p>同时，我运行了两个监控脚本(<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/execute/monitor-sETH.js">sETH</a>&amp;<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/execute/monitor-sUSD.js">sUSD</a>)去获取链上的数据，获取最佳的bundle策略(例如：闪电贷x个ETH去清算前3个sETH贷款，再以同样的做法清算下面两个，以此类推）并生成我的合约需要的数据。我需要每个区块都运行，以防价格发生变化或者某人关闭了贷款，来改变我的最佳策略。把<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/data/sUSD-optimalConfig.json">结果</a>保存在本地。</p><p><strong>最后，我有一个</strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/bertmiller/sMEV/blob/main/execute/executor.js#L15https://github.com/bertmiller/sMEV/blob/main/execute/executor.js#L15"><strong>执行脚本</strong></a><strong>将会收到pending交易并流向我的机器人，加载最佳策略，编写bundle并发送到Flashbots。</strong> 剩下的就是等待了。在这段时间内，最大的sETH贷款被借款人偿还，因此我关闭了部分机器人。一些最大的sUSD贷款也被关闭了，这显著的降低了潜在的钱款。</p><h3 id="h-6" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">第 6 步：关键时刻到来</h3><p>有趣的是有人试图发送相关合约的交易去诱饵机器人让它在早期失灵。我不确定这是否对别的机器人生效，但我的机器人没有上钩。 几个小时过后，真正的交易从pDAO发出了。经过几周的研究和准备，关键时刻来临了。一切都进展顺利在我这边，我的监控脚本运行完美，交易被成功捕获到，bundle成功创建并提交。</p><p>...然后，灾难发生了。连续几个区块都没有Flashbots块被挖出。我不止因此失去了机会，而且也没有Flashbots搜索者成功。没有Flashbots bundle在区块头部去阻止<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/tx/0x1e68bd612fd47b68ee01abb500e45f5dbf640e7efa9b92a1ee925c3a658d3341">企业级</a>的<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/tx/0x8296b0a8cae7e42f7989438c2ead9ea94d794930f9c456f5feff63758abe4b44">mempool</a><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/tx/0xaca5780d61aba2f58ce783f97511083a1ade323f8fe3f868518d4cbf85570ceb">机器人</a>进入并狙击到所有的有利润贷款。</p><p>尽管我输了，但我想我的方法是正确的。我的优势在于策略和找寻新机会，而不是参与PGAs（Priority Gas Auction最优gas费竞拍, 简称PGA）。因此使用Flashbots给了我获胜的最好机会，鉴于Flashbots的广泛使用，连续几个区块没有Flashbots块产出是有点不可因议的不走运了。</p><p>MEV有时候被看做是隐藏的超级科学家领域，但这并不一定。它很好玩，有趣并可模拟。然后游戏规则-可以说-如果你想寻找就是开放的。这篇文章是关于我学习游戏规则，开发这些规则对应策略并最终执行策略的过程。尽管我输了，但我学到了很多，并且享受这个过程。我希望我下次再来做，你也可以在下回加入我。 gg，mempool机器人，你赢了这次。但下回我会赢。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[以太坊闪电贷FlashLoan+去中心化交易所Swap实现]]></title>
            <link>https://paragraph.com/@java-3/flashloan-swap</link>
            <guid>HrI09H7ufWkAV3nomUYy</guid>
            <pubDate>Fri, 04 Nov 2022 08:28:15 GMT</pubDate>
            <description><![CDATA[碎碎念：闪电贷前面说的，讲究的是一个快进快出，空手套白狼，享受那12秒钟的极致暴富快感。 这12秒钟可以做很多的事情。就好像昨天晚上，东八区的币圈韭菜们都在沉睡，而美东时间的老银币却靠着GALA使劲搞钱。 不过，他们收获了金钱，我们收获了良好的睡眠质量，美好的未来理应面面俱到。 所以，这个事情告诉大家，记得多熬夜，熬夜真的能暴富。好了，话说回来。 有了闪电贷之后，再有了去中心化交易所，我们便可以进行三明治或者背后套利。 再次之前，我们先做的准备就是可以把两者结合起来。先和机器作斗争，再和人作斗争。]]></description>
            <content:encoded><![CDATA[<p>碎碎念：闪电贷前面说的，讲究的是一个快进快出，空手套白狼，享受那12秒钟的极致暴富快感。</p><p>这12秒钟可以做很多的事情。就好像昨天晚上，东八区的币圈韭菜们都在沉睡，而美东时间的老银币却靠着GALA使劲搞钱。</p><p>不过，他们收获了金钱，我们收获了良好的睡眠质量，美好的未来理应面面俱到。</p><p>所以，这个事情告诉大家，记得多熬夜，熬夜真的能暴富。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/e927555ff804fb1dd40b698a91ae3d5244c84f765cf9072830992960cb94fca8.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>好了，话说回来。</p><p>有了闪电贷之后，再有了去中心化交易所，我们便可以进行三明治或者背后套利。</p><p>再次之前，我们先做的准备就是可以把两者结合起来。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">先和机器作斗争，再和人作斗争。</h3>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[dex的Swap操作]]></title>
            <link>https://paragraph.com/@java-3/dex-swap</link>
            <guid>KgkUHBvrZnONEU7gpUNe</guid>
            <pubDate>Fri, 04 Nov 2022 08:15:44 GMT</pubDate>
            <description><![CDATA[想快速换钱实现兑换。只能够在去中心化交易所（DEX）进行。 在这里鄙视一下GATEIO，入账最少半个小时以上，黄花菜都凉了~ 现在排名前几的DEX有uni,sushi,curve。我们先选uni和curve。 说真的，curve官网文档配色真的是反人类。 https://curve.readthedocs.io/exchange-pools.html#exchange-pools-plain 还有uniswap。 https://docs.uniswap.org/protocol/guides/flash-integrations/inheritance-constructors 开始看他们的调用示例来决定我们的合约代码应该需要怎么写，扩展参数应该有哪些！ 找到他们的池子合约地址。 curve的有两个： 0xd51a44d3fae010294c616388b506acda1bfaae46 0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5etherscan.io点看查看源码，发现又臭又长。决定放弃阅读。结合文档，拿到exchange的相关信息，然...]]></description>
            <content:encoded><![CDATA[<p>想快速换钱实现兑换。只能够在去中心化交易所（DEX）进行。</p><p>在这里鄙视一下GATEIO，入账最少半个小时以上，黄花菜都凉了~</p><p>现在排名前几的DEX有uni,sushi,curve。我们先选uni和curve。</p><p>说真的，curve官网文档配色真的是反人类。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://curve.readthedocs.io/exchange-pools.html#exchange-pools-plain">https://curve.readthedocs.io/exchange-pools.html#exchange-pools-plain</a></p><p>还有uniswap。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.uniswap.org/protocol/guides/flash-integrations/inheritance-constructors">https://docs.uniswap.org/protocol/guides/flash-integrations/inheritance-constructors</a></p><p>开始看他们的调用示例来决定我们的合约代码应该需要怎么写，扩展参数应该有哪些！</p><p>找到他们的池子合约地址。</p><p>curve的有两个：</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0xd51a44d3fae010294c616388b506acda1bfaae46">0xd51a44d3fae010294c616388b506acda1bfaae46</a> <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://etherscan.io/address/0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5">0x80466c64868e1ab14a1ddf27a676c3fcbe638fe5</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/62b3d8d7bbc2c757d773ee77cc87f56a12b19184460aca1e5059305c881bb6ed.png" alt="etherscan.io" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">etherscan.io</figcaption></figure><p>点看查看源码，发现又臭又长。决定放弃阅读。结合文档，拿到exchange的相关信息，然后定义外部接口</p><pre data-type="codeBlock" text="//Curve合约接口
interface ICurveSwap {
    /**
    fromIndex,要发送代币的索引值
    toIndex,要接收代币的索引值
    fromAmount,需要兑换的代币数量
    dyAmount,获得的最小代币数量
    **/
    function exchange(uint256 fromIndex, uint256 toIndex, uint256 fromAmount, uint256 dyAmount) external payable;
    /**
    计算获得的最小代币数量
   fromIndex,要发送代币的索引值
   toIndex,要接收代币的索引值
   fromAmount,需要兑换的代币数量
   **/
    function get_dy(uint256 fromIndex, uint256 toIndex, uint256 fromAmount) external view returns (uint256);
}
"><code><span class="hljs-comment">//Curve合约接口</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ICurveSwap</span> </span>{
    <span class="hljs-comment">/**
    fromIndex,要发送代币的索引值
    toIndex,要接收代币的索引值
    fromAmount,需要兑换的代币数量
    dyAmount,获得的最小代币数量
    **/</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">exchange</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> fromIndex, <span class="hljs-keyword">uint256</span> toIndex, <span class="hljs-keyword">uint256</span> fromAmount, <span class="hljs-keyword">uint256</span> dyAmount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
    <span class="hljs-comment">/**
    计算获得的最小代币数量
   fromIndex,要发送代币的索引值
   toIndex,要接收代币的索引值
   fromAmount,需要兑换的代币数量
   **/</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">get_dy</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> fromIndex, <span class="hljs-keyword">uint256</span> toIndex, <span class="hljs-keyword">uint256</span> fromAmount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
}
</code></pre><p>接下来是uniswap，同样找到相关合约地址：</p><p>0x4e68ccd3e89f51c3074ca5072bbac773960dfa36</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a2c82109444cef810512e389445da9ba2da070a087091d0bbbac28a97dc34670.png" alt="info.uniswap.org" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">info.uniswap.org</figcaption></figure><h2 id="h-ethusdt" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">记住，我们找的是ETH/USDT的。</h2><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.uniswap.org/protocol/reference/core/UniswapV3Pool">https://docs.uniswap.org/protocol/reference/core/UniswapV3Pool</a></p><p>关于文档，uniswap真的比curve好太多太多了。</p><pre data-type="codeBlock" text="interface IUniSwap {

    /** recipient 接收地址
        zeroForOne 交换的方向，token0-&gt;token1为 true，token1-&gt;token0为 false
        amountSpecified 交换的数量，数值可正可负
        sqrtPriceLimitX96 价格平方根的Q64.96，如果是token0/token1方向的兑换，价格不能低于该参数。 如果是token1/token0方向，则不能大于该参数。
        data 回调函数参数
    **/
    function swap(
        address recipient,
        bool zeroForOne,
        int256 amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes data
    ) external returns (int256 amount0, int256 amount1);
}
"><code><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IUniSwap</span> </span>{

    <span class="hljs-comment">/** recipient 接收地址
        zeroForOne 交换的方向，token0->token1为 true，token1->token0为 false
        amountSpecified 交换的数量，数值可正可负
        sqrtPriceLimitX96 价格平方根的Q64.96，如果是token0/token1方向的兑换，价格不能低于该参数。 如果是token1/token0方向，则不能大于该参数。
        data 回调函数参数
    **/</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swap</span>(<span class="hljs-params">
        <span class="hljs-keyword">address</span> recipient,
        <span class="hljs-keyword">bool</span> zeroForOne,
        <span class="hljs-keyword">int256</span> amountSpecified,
        <span class="hljs-keyword">uint160</span> sqrtPriceLimitX96,
        <span class="hljs-keyword">bytes</span> data
    </span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">int256</span> amount0, <span class="hljs-keyword">int256</span> amount1</span>)</span>;
}
</code></pre><p>说实话，看官方解释，看得我一脸懵逼。sqrtPriceLimitX96这个参数太难理解了。其实就是xian价的意思。设置为0，则忽略。</p><p>返回值暂时不用管。</p><h3 id="h-curve" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">开始动手编写curve的兑换入口。</h3><pre data-type="codeBlock" text="function curveSwap(uint256 tokenIn, address tokenInAddress, uint256 tokenOut, uint256 amount) public {
        approve(CURVE_POOL, tokenInAddress, amount);
        ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, 0);
    }

    // 请求授权
    function approve(address requester, address token, uint256 amount) internal {
        uint256 alowance = IERC20(token).allowance(address(this), requester);
        if (alowance &lt; amount) {
            IERC20(token).safeApprove(requester, MAX_INT);
        }
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">curveSwap</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenIn, <span class="hljs-keyword">address</span> tokenInAddress, <span class="hljs-keyword">uint256</span> tokenOut, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        approve(CURVE_POOL, tokenInAddress, amount);
        ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, <span class="hljs-number">0</span>);
    }

    <span class="hljs-comment">// 请求授权</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> requester, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-keyword">uint256</span> alowance <span class="hljs-operator">=</span> IERC20(token).allowance(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), requester);
        <span class="hljs-keyword">if</span> (alowance <span class="hljs-operator">&#x3C;</span> amount) {
            IERC20(token).safeApprove(requester, MAX_INT);
        }
    }
</code></pre><p>因为curve要授权，所以在调用之前，确保已经进行授权。</p><p>比较疑惑的是tokenIn和tokenOut。这两个是池子定义的数组。在coins变量里面。内容如下</p><pre data-type="codeBlock" text="coins: constant(address[N_COINS]) = [
0xdAC17F958D2ee523a2206206994597C13D831ec7, //usdt index:0
0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, //wbtc
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, //weth index:2
]
"><code>coins: <span class="hljs-keyword">constant</span>(<span class="hljs-keyword">address</span>[N_COINS]) <span class="hljs-operator">=</span> [
<span class="hljs-number">0xdAC17F958D2ee523a2206206994597C13D831ec7</span>, <span class="hljs-comment">//usdt index:0</span>
<span class="hljs-number">0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599</span>, <span class="hljs-comment">//wbtc</span>
<span class="hljs-number">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>, <span class="hljs-comment">//weth index:2</span>
]
</code></pre><p>我们取的是索引值0和2的。WBTC不用管了。</p><h3 id="h-uniswap" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">接下来是uniswap的兑换接口</h3><pre data-type="codeBlock" text="function uniSwap(address tokenIn, address tokenOut, uint256 amount) public {
        //地址address类型本质上是一个160位的数字,可以进行加减
        bool zeroForOne = tokenIn &lt; tokenOut;
        //这行代码是抄的，回头再仔细想想是什么意思
        uint160 sqrtPriceLimitX96 = (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1);
        IUniSwap(UNISWAP_POOL).swap(address(this), zeroForOne, int256(amount), sqrtPriceLimitX96, abi.encode(amount));
    }

    //uni主动回调的函数
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) public {
        uint256 amount = abi.decode(_data, (uint256));
        IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
    }
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniSwap</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> tokenIn, <span class="hljs-keyword">address</span> tokenOut, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-comment">//地址address类型本质上是一个160位的数字,可以进行加减</span>
        <span class="hljs-keyword">bool</span> zeroForOne <span class="hljs-operator">=</span> tokenIn <span class="hljs-operator">&#x3C;</span> tokenOut;
        <span class="hljs-comment">//这行代码是抄的，回头再仔细想想是什么意思</span>
        <span class="hljs-keyword">uint160</span> sqrtPriceLimitX96 <span class="hljs-operator">=</span> (zeroForOne ? TickMath.MIN_SQRT_RATIO <span class="hljs-operator">+</span> <span class="hljs-number">1</span> : TickMath.MAX_SQRT_RATIO <span class="hljs-operator">-</span> <span class="hljs-number">1</span>);
        IUniSwap(UNISWAP_POOL).swap(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), zeroForOne, <span class="hljs-keyword">int256</span>(amount), sqrtPriceLimitX96, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount));
    }

    <span class="hljs-comment">//uni主动回调的函数</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniswapV3SwapCallback</span>(<span class="hljs-params"><span class="hljs-keyword">int256</span> amount0Delta, <span class="hljs-keyword">int256</span> amount1Delta, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(_data, (<span class="hljs-keyword">uint256</span>));
        IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
    }
</code></pre><p>因为uniswap餐用的是闪兑功能。需要我们提供一个回调函数uniswapV3SwapCallback。</p><p>我们在回调函数里面把钱给他转过去。</p><p>好了。写完之后便可以开始测试。</p><p>接下来命令一条龙服务：</p><pre data-type="codeBlock" text="
instance = await MoneyBot.deployed();
//发送10以太币
instance.send(web3.utils.toWei(&apos;10&apos;, &apos;ether&apos;))
//把eth转为WETH
instance.ETHtoWETH(web3.utils.toWei(&apos;8&apos;, &apos;ether&apos;));
//查看合约里面的WETH余额
contractWethBalance  = await instance.getThisWethBalance();
contractWethBalance.toString();
//查看合约里面的USDT余额
contractUSDTBalance  = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
//定义两个合约地址变量
WETHADDR=&quot;0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2&quot;;
USDTADDR=&quot;0xdAC17F958D2ee523a2206206994597C13D831ec7&quot;;
//测试uni闪兑功能。
instance.uniSwap(WETHADDR,USDTADDR,web3.utils.toWei(&apos;1&apos;));
//再次查看合约里面的USDT余额，看看是否有改变。
contractUSDTBalance  = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();


//测试curve的exchange，从WETH兑换到USDT
instance.curveSwap(2,WETHADDR,0,web3.utils.toWei(&apos;2&apos;));
//再次查看合约里面的USDT余额，看看是否有改变。
contractUSDTBalance  = await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
"><code>
instance <span class="hljs-operator">=</span> await MoneyBot.deployed();
<span class="hljs-comment">//发送10以太币</span>
instance.<span class="hljs-built_in">send</span>(web3.utils.toWei(<span class="hljs-string">'10'</span>, <span class="hljs-string">'ether'</span>))
<span class="hljs-comment">//把eth转为WETH</span>
instance.ETHtoWETH(web3.utils.toWei(<span class="hljs-string">'8'</span>, <span class="hljs-string">'ether'</span>));
<span class="hljs-comment">//查看合约里面的WETH余额</span>
contractWethBalance  <span class="hljs-operator">=</span> await instance.getThisWethBalance();
contractWethBalance.toString();
<span class="hljs-comment">//查看合约里面的USDT余额</span>
contractUSDTBalance  <span class="hljs-operator">=</span> await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
<span class="hljs-comment">//定义两个合约地址变量</span>
WETHADDR<span class="hljs-operator">=</span><span class="hljs-string">"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"</span>;
USDTADDR<span class="hljs-operator">=</span><span class="hljs-string">"0xdAC17F958D2ee523a2206206994597C13D831ec7"</span>;
<span class="hljs-comment">//测试uni闪兑功能。</span>
instance.uniSwap(WETHADDR,USDTADDR,web3.utils.toWei(<span class="hljs-string">'1'</span>));
<span class="hljs-comment">//再次查看合约里面的USDT余额，看看是否有改变。</span>
contractUSDTBalance  <span class="hljs-operator">=</span> await instance.getThisUSDTBalance();
contractUSDTBalance.toString();


<span class="hljs-comment">//测试curve的exchange，从WETH兑换到USDT</span>
instance.curveSwap(<span class="hljs-number">2</span>,WETHADDR,<span class="hljs-number">0</span>,web3.utils.toWei(<span class="hljs-string">'2'</span>));
<span class="hljs-comment">//再次查看合约里面的USDT余额，看看是否有改变。</span>
contractUSDTBalance  <span class="hljs-operator">=</span> await instance.getThisUSDTBalance();
contractUSDTBalance.toString();
</code></pre><p>OK，如果余额发生改变，证明兑换已经成功。</p><p>测试通过后，便可以再次进行优化代码。比如说定义一个统一接口swap，根据flag进行识别具体是要使用哪一个模块来兑换。我知道java程序员喜欢用字符串或者字典。但是solidity用字符串实在是牺牲太大。所以用一个uint8就可以！</p><p>下一节，准备开始把闪电贷和swap功能结合起来。</p><p>附上主类的代码。</p><pre data-type="codeBlock" text="pragma solidity ^0.8.17;

import &quot;./Libs.sol&quot;;
import &quot;./AddressList.sol&quot;;

contract MoneyBot {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    //合约的部署者
    address owner;
    uint256 MAX_INT = 2 ** 256 - 1;

    constructor(){
        owner = address(tx.origin);
    }

    // 返回合约的所有者
    function getOwner() public view returns (address) {
        return owner;
    }
    // 返回某个账户的某个token的余额
    function getTokenBalance(address token, address account) public view returns (uint256) {
        return IERC20(token).balanceOf(account);
    }
    // 返回合约部署者账户中的ETH余额
    function getMyWethBalance() public view returns (uint256) {
        return IERC20(WETH).balanceOf(owner);
    }
    // 返回合约部署者账户中的USDT余额
    function getMyUSDTBalance() public view returns (uint256) {
        return IERC20(USDT).balanceOf(owner);
    }
    // 返回合约账户中的WETH余额
    function getThisWethBalance() public view returns (uint256) {
        return IERC20(WETH).balanceOf(address(this));
    }
    // 返回合约账户中的USDT余额
    function getThisUSDTBalance() public view returns (uint256) {
        return IERC20(USDT).balanceOf(address(this));
    }

    //把合约里面的ETH转到钱包
    function turnOutETH(uint256 amount) public onlyOwner {
        payable(owner).transfer(amount);
    }
    //把合约里面的token转到钱包
    function turnOutToken(address token, uint256 amount) public onlyOwner {
        IERC20(token).transfer(owner, amount);
    }
    // WETH转换为ETH
    function WETHToETH(uint256 amount) public onlyOwner {
        IWETH(WETH).withdraw(amount);
    }
    // ETH转换为WETH
    function ETHtoWETH(uint256 amount) public onlyOwner {
        IWETH(WETH).deposit{value : amount}();
    }


    function curveSwap(uint256 tokenIn, address tokenInAddress, uint256 tokenOut, uint256 amount) public {
        approve(CURVE_POOL, tokenInAddress, amount);
        ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, 0);
    }

    // 请求授权
    function approve(address requester, address token, uint256 amount) internal {
        uint256 alowance = IERC20(token).allowance(address(this), requester);
        if (alowance &lt; amount) {
            IERC20(token).safeApprove(requester, MAX_INT);
        }
    }

    function uniSwap(address tokenIn, address tokenOut, uint256 amount) public {
        //地址address类型本质上是一个160位的数字,可以进行加减
        bool zeroForOne = tokenIn &lt; tokenOut;
        //这行代码是抄的，回头再仔细想想是什么意思
        uint160 sqrtPriceLimitX96 = (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1);
        IUniSwap(UNISWAP_POOL).swap(address(this), zeroForOne, int256(amount), sqrtPriceLimitX96, abi.encode(amount));
    }

    //uni主动回调的函数
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) public {
        uint256 amount = abi.decode(_data, (uint256));
        IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
    }



    //发起一个eth的闪电贷
    function doFlashEth(uint256 amount) public onlyOwner {
        ILiquidity(LIQUIDITY_POOL).borrow(WETH, amount,
            abi.encodeWithSelector(this.receiveBuyBigHouse.selector, abi.encode(amount)));
    }

    // callback ,收到钱后可以拿来买一个大房子。
    function receiveBuyBigHouse(bytes memory data) public {
        require(msg.sender == BORROWER_PROXY, &quot;bu shi BorrorwerProxy,paochu yichang.&quot;);
        //购买大房子
        uint256 amount = abi.decode(data, (uint256));
        IERC20(WETH).transfer(LIQUIDITY_POOL, amount);
    }



    //fallback
    receive() external payable {}
    // modifier
    modifier onlyOwner(){
        require(address(msg.sender) == owner, &quot;No authority&quot;);
        _;
    }
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.17;</span>

<span class="hljs-keyword">import</span> <span class="hljs-string">"./Libs.sol"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./AddressList.sol"</span>;

<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">MoneyBot</span> </span>{
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeMath</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title"><span class="hljs-keyword">uint256</span></span>;
    <span class="hljs-keyword">using</span> <span class="hljs-title">SafeERC20</span> <span class="hljs-title"><span class="hljs-keyword">for</span></span> <span class="hljs-title">IERC20</span>;

    <span class="hljs-comment">//合约的部署者</span>
    <span class="hljs-keyword">address</span> owner;
    <span class="hljs-keyword">uint256</span> MAX_INT <span class="hljs-operator">=</span> <span class="hljs-number">2</span> <span class="hljs-operator">*</span><span class="hljs-operator">*</span> <span class="hljs-number">256</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>);
    }

    <span class="hljs-comment">// 返回合约的所有者</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> owner;
    }
    <span class="hljs-comment">// 返回某个账户的某个token的余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTokenBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(account);
    }
    <span class="hljs-comment">// 返回合约部署者账户中的ETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMyWethBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(WETH).balanceOf(owner);
    }
    <span class="hljs-comment">// 返回合约部署者账户中的USDT余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMyUSDTBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(USDT).balanceOf(owner);
    }
    <span class="hljs-comment">// 返回合约账户中的WETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getThisWethBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(WETH).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }
    <span class="hljs-comment">// 返回合约账户中的USDT余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getThisUSDTBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(USDT).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
    }

    <span class="hljs-comment">//把合约里面的ETH转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-keyword">payable</span>(owner).<span class="hljs-built_in">transfer</span>(amount);
    }
    <span class="hljs-comment">//把合约里面的token转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IERC20(token).<span class="hljs-built_in">transfer</span>(owner, amount);
    }
    <span class="hljs-comment">// WETH转换为ETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">WETHToETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).withdraw(amount);
    }
    <span class="hljs-comment">// ETH转换为WETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ETHtoWETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).deposit{value : amount}();
    }


    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">curveSwap</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> tokenIn, <span class="hljs-keyword">address</span> tokenInAddress, <span class="hljs-keyword">uint256</span> tokenOut, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        approve(CURVE_POOL, tokenInAddress, amount);
        ICurveSwap(CURVE_POOL).exchange(tokenIn, tokenOut, amount, <span class="hljs-number">0</span>);
    }

    <span class="hljs-comment">// 请求授权</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> requester, <span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> </span>{
        <span class="hljs-keyword">uint256</span> alowance <span class="hljs-operator">=</span> IERC20(token).allowance(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), requester);
        <span class="hljs-keyword">if</span> (alowance <span class="hljs-operator">&#x3C;</span> amount) {
            IERC20(token).safeApprove(requester, MAX_INT);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniSwap</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> tokenIn, <span class="hljs-keyword">address</span> tokenOut, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-comment">//地址address类型本质上是一个160位的数字,可以进行加减</span>
        <span class="hljs-keyword">bool</span> zeroForOne <span class="hljs-operator">=</span> tokenIn <span class="hljs-operator">&#x3C;</span> tokenOut;
        <span class="hljs-comment">//这行代码是抄的，回头再仔细想想是什么意思</span>
        <span class="hljs-keyword">uint160</span> sqrtPriceLimitX96 <span class="hljs-operator">=</span> (zeroForOne ? TickMath.MIN_SQRT_RATIO <span class="hljs-operator">+</span> <span class="hljs-number">1</span> : TickMath.MAX_SQRT_RATIO <span class="hljs-operator">-</span> <span class="hljs-number">1</span>);
        IUniSwap(UNISWAP_POOL).swap(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>), zeroForOne, <span class="hljs-keyword">int256</span>(amount), sqrtPriceLimitX96, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount));
    }

    <span class="hljs-comment">//uni主动回调的函数</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">uniswapV3SwapCallback</span>(<span class="hljs-params"><span class="hljs-keyword">int256</span> amount0Delta, <span class="hljs-keyword">int256</span> amount1Delta, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(_data, (<span class="hljs-keyword">uint256</span>));
        IERC20(WETH).safeTransfer(UNISWAP_POOL, amount);
    }



    <span class="hljs-comment">//发起一个eth的闪电贷</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doFlashEth</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        ILiquidity(LIQUIDITY_POOL).borrow(WETH, amount,
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.receiveBuyBigHouse.<span class="hljs-built_in">selector</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount)));
    }

    <span class="hljs-comment">// callback ,收到钱后可以拿来买一个大房子。</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">receiveBuyBigHouse</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> BORROWER_PROXY, <span class="hljs-string">"bu shi BorrorwerProxy,paochu yichang."</span>);
        <span class="hljs-comment">//购买大房子</span>
        <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">uint256</span>));
        IERC20(WETH).<span class="hljs-built_in">transfer</span>(LIQUIDITY_POOL, amount);
    }



    <span class="hljs-comment">//fallback</span>
    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
    <span class="hljs-comment">// modifier</span>
    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner, <span class="hljs-string">"No authority"</span>);
        <span class="hljs-keyword">_</span>;
    }
}
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/0edc57f95597bcd2aef43ab83f42ef81cfd45a6b3384bd91dc99b0828e8fa319.png" alt="工程目录" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">工程目录</figcaption></figure><p>最后，大家记得帮忙众筹6颗核桃啊~。脑子不够用了。</p><p>点击转账三百块！</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[ETH测试网进行闪电贷测试]]></title>
            <link>https://paragraph.com/@java-3/eth</link>
            <guid>CAUBagRjDyfnvjsfDo3X</guid>
            <pubDate>Wed, 02 Nov 2022 06:51:21 GMT</pubDate>
            <description><![CDATA[我们在编写完成闪电贷的例子之后，便可以开始编译和部署了。 不过，写程序就是这样子的，你写着写着，发现不时地可以优化一下。所以，我又要改代码了。不要紧张，改完后会自测的。反正现在也不会有测试妹子抓着你不放。 添加以下几行代码，方便测试。// 返回某个账户的某个token的余额 function getTokenBalance(address token, address account) public view returns (uint256) { return IERC20(token).balanceOf(account); } // 返回合约部署者账户中的WETH余额 function getMyWethBalance() public view returns (uint256) { return IERC20(WETH).balanceOf(owner); } // 返回合约账户中的WETH余额 function getContractWethBalance(address account) public view returns (uint256) { return ...]]></description>
            <content:encoded><![CDATA[<p>我们在编写完成闪电贷的例子之后，便可以开始编译和部署了。</p><p>不过，写程序就是这样子的，你写着写着，发现不时地可以优化一下。所以，我又要改代码了。不要紧张，改完后会自测的。反正现在也不会有测试妹子抓着你不放。</p><p>添加以下几行代码，方便测试。</p><pre data-type="codeBlock" text="// 返回某个账户的某个token的余额
    function getTokenBalance(address token, address account) public view returns (uint256) {
        return IERC20(token).balanceOf(account);
    }
    // 返回合约部署者账户中的WETH余额
    function getMyWethBalance() public view returns (uint256) {
        return IERC20(WETH).balanceOf(owner);
    }
    // 返回合约账户中的WETH余额
    function getContractWethBalance(address account) public view returns (uint256) {
        return getTokenBalance(account);
    }
"><code><span class="hljs-comment">// 返回某个账户的某个token的余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTokenBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(account);
    }
    <span class="hljs-comment">// 返回合约部署者账户中的WETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMyWethBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(WETH).balanceOf(owner);
    }
    <span class="hljs-comment">// 返回合约账户中的WETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getContractWethBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> getTokenBalance(account);
    }
</code></pre><p>完成之后，开始进行测试。这次是真的不改代码了！</p><p>启动测试服务器。</p><pre data-type="codeBlock" text="nohup ganache-cli --fork https://eth-mainnet.g.alchemy.com/v2/申请的Key &amp;
"><code>nohup ganache<span class="hljs-operator">-</span>cli <span class="hljs-operator">-</span><span class="hljs-operator">-</span>fork https:<span class="hljs-comment">//eth-mainnet.g.alchemy.com/v2/申请的Key &#x26;</span>
</code></pre><p>该命令不需要你另外再打开一个新的终端。对了，记得去以下网站申请一个测试KEY</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.alchemy.com/">https://www.alchemy.com/</a></p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">接下来搓搓小手，开始一条路敲命令了！</h2><pre data-type="codeBlock" text="truffle complie
truffle migrate
truffle console
"><code>truffle complie
truffle migrate
truffle <span class="hljs-variable language_">console</span>
</code></pre><p>3浪打完，进入控制台。</p><p>先启动一个合约实例对象。</p><pre data-type="codeBlock" text="instance = await HelloZZ.deployed();
"><code><span class="hljs-attr">instance</span> = await HelloZZ.deployed()<span class="hljs-comment">;</span>
</code></pre><p>心里有些好奇，看一下合约地址。</p><pre data-type="codeBlock" text="instance.address
"><code>instance.<span class="hljs-built_in">address</span>
</code></pre><p>也可以看一下你是谁。也就是合约的部署者。</p><pre data-type="codeBlock" text="owner = await instance.getOwner();
"><code><span class="hljs-attr">owner</span> = await instance.getOwner()<span class="hljs-comment">;</span>
</code></pre><p>查询自己的余额</p><pre data-type="codeBlock" text="查询余额
myBalance = await instance.getMyWethBalance();
myBalance.toString();
"><code>查询余额
myBalance <span class="hljs-operator">=</span> await instance.getMyWethBalance();
myBalance.toString();
</code></pre><p>虽然什么都没有，但是我相信这是一个好习惯。</p><p>向合约转50个ETH。</p><pre data-type="codeBlock" text="instance.send(web3.utils.toWei(&apos;50&apos;, &apos;ether&apos;))
"><code>instance.<span class="hljs-built_in">send</span>(web3.utils.toWei(<span class="hljs-string">'50'</span>, <span class="hljs-string">'ether'</span>))
</code></pre><p>然后把ETH转成WETH</p><pre data-type="codeBlock" text="instance.ETHtoWETH(web3.utils.toWei(&apos;50&apos;, &apos;ether&apos;));
"><code>instance.ETHtoWETH(web3.utils.toWei(<span class="hljs-string">'50'</span>, <span class="hljs-string">'ether'</span>));
</code></pre><p>查看合约里面的WETH余额</p><pre data-type="codeBlock" text="contractWeth = await instance.getContractWethBalance(instance.address);
contractWeth.toString();
"><code>contractWeth <span class="hljs-operator">=</span> await instance.getContractWethBalance(instance.<span class="hljs-built_in">address</span>);
contractWeth.toString();
</code></pre><p>调用合约的turnOutToken()函数，把WETH转出到所有者的钱包里</p><pre data-type="codeBlock" text="instance.turnOutToken(WETH, contractWeth)
"><code>instance.turnOutToken(WETH, contractWeth)
</code></pre><p>再次检查一下你的钱包余额。</p><pre data-type="codeBlock" text="myBalance = await instance.getMyWethBalance();
myBalance.toString();
"><code>myBalance <span class="hljs-operator">=</span> await instance.getMyWethBalance();
myBalance.toString();
</code></pre><p>一切顺利之下，现在可以开始进行测试发起闪电贷。</p><pre data-type="codeBlock" text="result = await instance.doFlashEth(WETH, web3.utils.toWei(&apos;100&apos;))
result
"><code>result <span class="hljs-operator">=</span> await instance.doFlashEth(WETH, web3.utils.toWei(<span class="hljs-string">'100'</span>))
result
</code></pre><p>控制台会告诉你是否成功的。贷款数量也不要修改。比如你想要1000个ETH（我也想），会提示low-level call failed。该问题疑似余额不够。</p><p>OK，最后附上修改后的完整代码。还有很熟悉的一段话：如果文档和代码不相符，以代码为准[doge]</p><pre data-type="codeBlock" text="pragma solidity ^0.8.17;
import &quot;./ExInterface.sol&quot;;
contract HelloZZ {

     //** **定义一些常量 开始 **//
    address WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address liquidityPool = 0x4F868C1aa37fCf307ab38D215382e88FCA6275E2;
    address borrowerProxy = 0x17a4C8F43cB407dD21f9885c5289E66E21bEcD9D;
    //** **定义一些常量 结束 **//

    //合约的部署者
    address owner;

    constructor(){
        owner = address(tx.origin);
   }


    // 返回合约的所有者
    function getOwner() public view returns(address) {
        return owner;
    }// 返回某个账户的某个token的余额
    function getTokenBalance(address token, address account) public view returns(uint256) {
        return IERC20(token).balanceOf(account);
    }
    // 返回合约部署者账户中的ETH余额
    function getMyWethBalance() public view returns (uint256) {
        return IERC20(WETH).balanceOf(owner);
    }
    // 返回合约账户中的WETH余额
    function getContractWethBalance(address account) public view returns (uint256) {
        return getTokenBalance(account);
    }
    //把合约里面的ETH转到钱包
    function turnOutETH(uint256 amount) public onlyOwner {
        payable(owner).transfer(amount);
    }
    //把合约里面的钱转到钱包
    function turnOutToken(address token, uint256 amount) public onlyOwner {
        IERC20(token).transfer(owner, amount);
    }
    // WETH转换为ETH
    function WETHToETH(uint256 amount) public onlyOwner {
        IWETH(WETH).withdraw(amount);
    }
    // ETH转换为WETH
    function ETHtoWETH(uint256 amount) public onlyOwner {
        IWETH(WETH).deposit{value : amount}();
    }

    //发起一个eth的闪电贷
    function doFlashEth(uint256 amount)public onlyOwner{
        ILiquidity(liquidityPool).borrow(WETH, amount,
            abi.encodeWithSelector(this.receiveBuyBigHouse.selector, abi.encode(amount)));
    }

    // callback ,收到钱后可以拿来买一个大房子。
    function receiveBuyBigHouse(bytes memory data) public {
        require(msg.sender == borrowerProxy, &quot;bu shi BorrorwerProxy,paochu yichang.&quot;);
        //购买大房子
        uint256 amount = abi.decode(data, (uint256));
        IERC20(WETH).transfer(liquidityPool, amount);
    }

    //fallback
    receive() external payable {}
    // modifier
    modifier onlyOwner(){
        require(address(msg.sender) == owner, &quot;No authority&quot;);
        _;
    }
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.17;</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">"./ExInterface.sol"</span>;
<span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">HelloZZ</span> </span>{

     <span class="hljs-comment">//** **定义一些常量 开始 **//</span>
    <span class="hljs-keyword">address</span> WETH <span class="hljs-operator">=</span> <span class="hljs-number">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>;
    <span class="hljs-keyword">address</span> liquidityPool <span class="hljs-operator">=</span> <span class="hljs-number">0x4F868C1aa37fCf307ab38D215382e88FCA6275E2</span>;
    <span class="hljs-keyword">address</span> borrowerProxy <span class="hljs-operator">=</span> <span class="hljs-number">0x17a4C8F43cB407dD21f9885c5289E66E21bEcD9D</span>;
    <span class="hljs-comment">//** **定义一些常量 结束 **//</span>

    <span class="hljs-comment">//合约的部署者</span>
    <span class="hljs-keyword">address</span> owner;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>);
   }


    <span class="hljs-comment">// 返回合约的所有者</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">address</span></span>) </span>{
        <span class="hljs-keyword">return</span> owner;
    }<span class="hljs-comment">// 返回某个账户的某个token的余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTokenBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(token).balanceOf(account);
    }
    <span class="hljs-comment">// 返回合约部署者账户中的ETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getMyWethBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> IERC20(WETH).balanceOf(owner);
    }
    <span class="hljs-comment">// 返回合约账户中的WETH余额</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getContractWethBalance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>) </span>{
        <span class="hljs-keyword">return</span> getTokenBalance(account);
    }
    <span class="hljs-comment">//把合约里面的ETH转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-keyword">payable</span>(owner).<span class="hljs-built_in">transfer</span>(amount);
    }
    <span class="hljs-comment">//把合约里面的钱转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IERC20(token).<span class="hljs-built_in">transfer</span>(owner, amount);
    }
    <span class="hljs-comment">// WETH转换为ETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">WETHToETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).withdraw(amount);
    }
    <span class="hljs-comment">// ETH转换为WETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ETHtoWETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).deposit{value : amount}();
    }

    <span class="hljs-comment">//发起一个eth的闪电贷</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doFlashEth</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>)<span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span></span>{
        ILiquidity(liquidityPool).borrow(WETH, amount,
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.receiveBuyBigHouse.<span class="hljs-built_in">selector</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount)));
    }

    <span class="hljs-comment">// callback ,收到钱后可以拿来买一个大房子。</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">receiveBuyBigHouse</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> borrowerProxy, <span class="hljs-string">"bu shi BorrorwerProxy,paochu yichang."</span>);
        <span class="hljs-comment">//购买大房子</span>
        <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">uint256</span>));
        IERC20(WETH).<span class="hljs-built_in">transfer</span>(liquidityPool, amount);
    }

    <span class="hljs-comment">//fallback</span>
    <span class="hljs-function"><span class="hljs-keyword">receive</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{}
    <span class="hljs-comment">// modifier</span>
    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner, <span class="hljs-string">"No authority"</span>);
        <span class="hljs-keyword">_</span>;
    }
}
</code></pre><p>对了。记得帮我众筹8个核桃啊。谢谢大家。</p><p>下一节，将会带领大家玩耍闪电贷+套利。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[闪电贷学习和编写]]></title>
            <link>https://paragraph.com/@java-3/in5NdNN5IICqTfyKm8xB</link>
            <guid>in5NdNN5IICqTfyKm8xB</guid>
            <pubDate>Tue, 01 Nov 2022 08:18:18 GMT</pubDate>
            <description><![CDATA[1.什么是闪电贷？关于闪电贷的概念，网络上有很多。一句话总结就是一个区块内借款和还款同时完成。 听起来第一个想法就是：有个毛用？ 对，普通情况下就是没卵用。但是没用也要去学习他。为了学习它，总得找个理由让自己打个鸡血。 emmm,想到了！ 那就是发起一笔闪电贷，拿来买大房子。这样子我就可以拥有它1-12秒钟！2.和谁贷款？提供闪电贷的很多。有aave，uni,keeperdao等等。。 其他的相对复杂，就先拿keeperDao来举例吧。因为它最简单，也是yueying大佬最先开始写的。 它的合约地址是3.怎么借？知道合约地址了可以查看代码，还有去官网看说明。 最主要的函数/// @notice borrow assets from this LP, and return them within the same transaction. /// /// @param _token The address of the token contract. /// @param _amount The amont of token. /// @param _data The implem...]]></description>
            <content:encoded><![CDATA[<h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.什么是闪电贷？</h3><p>关于闪电贷的概念，网络上有很多。一句话总结就是一个区块内借款和还款同时完成。</p><p>听起来第一个想法就是：有个毛用？</p><p>对，普通情况下就是没卵用。但是没用也要去学习他。为了学习它，总得找个理由让自己打个鸡血。</p><p>emmm,想到了！</p><p>那就是发起一笔闪电贷，拿来买大房子。这样子我就可以拥有它1-12秒钟！</p><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.和谁贷款？</h3><p>提供闪电贷的很多。有aave，uni,keeperdao等等。。</p><p>其他的相对复杂，就先拿keeperDao来举例吧。因为它最简单，也是yueying大佬最先开始写的。</p><p>它的合约地址是</p><pre data-type="codeBlock" text="0x4F868C1aa37fCf307ab38D215382e88FCA6275E2
"><code></code></pre><h3 id="h-3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.怎么借？</h3><p>知道合约地址了可以查看代码，还有去官网看说明。</p><p>最主要的函数</p><pre data-type="codeBlock" text="/// @notice borrow assets from this LP, and return them within the       same transaction.
    ///
    /// @param _token The address of the token contract.
    /// @param _amount The amont of token.
    /// @param _data The implementation specific data for the Borrower.
    function borrow(address _token, uint256 _amount, bytes calldata _data) external nonReentrant whenNotPaused {
        require(address(kTokens[_token]) != address(0x0), &quot;Token is not registered&quot;);
        uint256 initialBalance = borrowableBalance(_token);
        _transferOut(_msgSender(), _token, _amount);
        borrower.lend(_msgSender(), _data);
        uint256 finalBalance = borrowableBalance(_token);
        require(finalBalance &gt;= initialBalance, &quot;Borrower failed to return the borrowed funds&quot;);

        uint256 fee = finalBalance - initialBalance;
        uint256 poolFee = calculateFee(poolFeeInBips, fee);
        emit Borrowed(_msgSender(), _token, _amount, fee);
        _transferOut(feePool, _token, poolFee);
    }
"><code><span class="hljs-comment">/// @notice borrow assets from this LP, and return them within the       same transaction.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// @param _token The address of the token contract.</span>
    <span class="hljs-comment">/// @param _amount The amont of token.</span>
    <span class="hljs-comment">/// @param _data The implementation specific data for the Borrower.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">borrow</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _token, <span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title">whenNotPaused</span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(kTokens[_token]) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0x0</span>), <span class="hljs-string">"Token is not registered"</span>);
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> borrowableBalance(_token);
        _transferOut(_msgSender(), _token, _amount);
        borrower.lend(_msgSender(), _data);
        <span class="hljs-keyword">uint256</span> finalBalance <span class="hljs-operator">=</span> borrowableBalance(_token);
        <span class="hljs-built_in">require</span>(finalBalance <span class="hljs-operator">></span><span class="hljs-operator">=</span> initialBalance, <span class="hljs-string">"Borrower failed to return the borrowed funds"</span>);

        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> finalBalance <span class="hljs-operator">-</span> initialBalance;
        <span class="hljs-keyword">uint256</span> poolFee <span class="hljs-operator">=</span> calculateFee(poolFeeInBips, fee);
        <span class="hljs-keyword">emit</span> Borrowed(_msgSender(), _token, _amount, fee);
        _transferOut(feePool, _token, poolFee);
    }
</code></pre><p>好了。知道了主要的函数之后，就可以想一想应该怎么做了。</p><h3 id="h-4" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.开始写发起闪电贷的代码</h3><p>PS：再写之前，先去上个洗手间，洗洗手，泡杯咖啡。思路清晰之后再去做。写这篇文章的时候，我是在公司写的，有效利用摸鱼时间。毕竟我是十年java程序员，工作效率极高之下，时间多一点是很正常的。</p><p>先创建ExInterface.sol文件，里面主要编写引用的外部合约。文件内容如下：</p><pre data-type="codeBlock" text="pragma solidity ^0.8.17;

//ERC20标准token的接口
interface IERC20 {
    //总供应量(totalSupply)
    function totalSupply() external view returns (uint256);
    //余额(balanceOf)
    function balanceOf(address account) external view returns (uint256);
    //转账(transfer)
    function transfer(address recipient, uint256 amount) external returns (bool);
    //授权(approve)
    function approve(address spender, uint256 amount) external returns (bool);
    //请求转账(transferFrom)
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

    function decimals() external view returns (uint8);

    function allowance(address owner, address spender) external view returns (uint256);
}
//将以太坊原生代币ETH与ERC20token互相转换的合约
//合约地址 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
interface IWETH {
    //将ETH转换为WETH
    function deposit() external payable;
    //将ETH转换为WETH
    function withdraw(uint wad) external;
}

//KeeperDao的LiquidityPool合约
//合约地址0x4F868C1aa37fCf307ab38D215382e88FCA6275E2
interface ILiquidity {
    //_token 所借的代币名称；_amount 借多少金额；calldata 回调函数
    function borrow(address _token, uint256 _amount, bytes calldata _data) external;
}
"><code><span class="hljs-meta"><span class="hljs-keyword">pragma</span> <span class="hljs-keyword">solidity</span> ^0.8.17;</span>

<span class="hljs-comment">//ERC20标准token的接口</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IERC20</span> </span>{
    <span class="hljs-comment">//总供应量(totalSupply)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">totalSupply</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
    <span class="hljs-comment">//余额(balanceOf)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">balanceOf</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> account</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
    <span class="hljs-comment">//转账(transfer)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transfer</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> recipient, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
    <span class="hljs-comment">//授权(approve)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">approve</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> spender, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;
    <span class="hljs-comment">//请求转账(transferFrom)</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transferFrom</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> sender, <span class="hljs-keyword">address</span> recipient, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">bool</span></span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">decimals</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint8</span></span>)</span>;

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">allowance</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> owner, <span class="hljs-keyword">address</span> spender</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint256</span></span>)</span>;
}
<span class="hljs-comment">//将以太坊原生代币ETH与ERC20token互相转换的合约</span>
<span class="hljs-comment">//合约地址 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">IWETH</span> </span>{
    <span class="hljs-comment">//将ETH转换为WETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deposit</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span></span>;
    <span class="hljs-comment">//将ETH转换为WETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> wad</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}

<span class="hljs-comment">//KeeperDao的LiquidityPool合约</span>
<span class="hljs-comment">//合约地址0x4F868C1aa37fCf307ab38D215382e88FCA6275E2</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ILiquidity</span> </span>{
    <span class="hljs-comment">//_token 所借的代币名称；_amount 借多少金额；calldata 回调函数</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">borrow</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _token, <span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span></span>;
}
</code></pre><p>相信代码解释已经足够清晰。这里就不再详细解释。最主要的是ILiquidity 接口的borrow()一眼就能看懂了吧。</p><p>至于IERC20 和IWETH 为什么要这么写，也暂时不用去管它。</p><p>然后，创建我们的主要合约文件FlashLoan.sol</p><p>PS：前辈们说的，只要你写合约，第一件事就是先写上代币转出的功能。</p><p>所以，求生欲望极强的我，马上写上(复制)以下几个函数</p><pre data-type="codeBlock" text=" //把合约里面的ETH转到钱包
    function turnOutETH(uint256 amount) public onlyOwner {
        payable(owner).transfer(amount);
    }
    //把合约里面的钱转到钱包
    function turnOutToken(address token, uint256 amount) public onlyOwner {
        IERC20(token).transfer(owner, amount);
    }
    // WETH转换为ETH
    function WETHToETH(uint256 amount) public onlyOwner {
        IWETH(WETH).withdraw(amount);
    }
    // ETH转换为WETH
    function ETHtoWETH(uint256 amount) public onlyOwner {
        IWETH(WETH).deposit{value : amount}();
    }
"><code> <span class="hljs-comment">//把合约里面的ETH转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        <span class="hljs-keyword">payable</span>(owner).<span class="hljs-built_in">transfer</span>(amount);
    }
    <span class="hljs-comment">//把合约里面的钱转到钱包</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">turnOutToken</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> token, <span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IERC20(token).<span class="hljs-built_in">transfer</span>(owner, amount);
    }
    <span class="hljs-comment">// WETH转换为ETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">WETHToETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).withdraw(amount);
    }
    <span class="hljs-comment">// ETH转换为WETH</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ETHtoWETH</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title">onlyOwner</span> </span>{
        IWETH(WETH).deposit{value : amount}();
    }
</code></pre><p>然后创建两个函数，一个是发起闪电贷的主函数，一个是接收闪电贷的回调函数。</p><pre data-type="codeBlock" text="    //发起一个eth的闪电贷。这里先不自定义传入token类别了
    function doFlashEth(uint256 amount){
address liquidityPool = 0x4F868C1aa37fCf307ab38D215382e88FCA6275E2;
        address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
        ILiquidity(liquidityPool).borrow(weth, amount,
            abi.encodeWithSelector(this.receiveBuyBigHouse.selector, abi.encode(amount)));
    }

    // callback ,收到钱后可以拿来买一个大房子。
    function receiveBuyBigHouse(bytes memory data) public {
        require(msg.sender == borrowerProxy, &quot;bu shi BorrorwerProxy,paochu yichang.&quot;);
//buy big house---&gt;.
address liquidityPool = 0x4F868C1aa37fCf307ab38D215382e88FCA6275E2;
        address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 amount=abi.decode(data, (uint256));
IERC20(weth).transfer(liquidityPool, amount);
    }
"><code>    <span class="hljs-comment">//发起一个eth的闪电贷。这里先不自定义传入token类别了</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doFlashEth</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>)</span>{
<span class="hljs-keyword">address</span> liquidityPool <span class="hljs-operator">=</span> <span class="hljs-number">0x4F868C1aa37fCf307ab38D215382e88FCA6275E2</span>;
        <span class="hljs-keyword">address</span> weth <span class="hljs-operator">=</span> <span class="hljs-number">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>;
        ILiquidity(liquidityPool).borrow(weth, amount,
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.receiveBuyBigHouse.<span class="hljs-built_in">selector</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount)));
    }

    <span class="hljs-comment">// callback ,收到钱后可以拿来买一个大房子。</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">receiveBuyBigHouse</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> borrowerProxy, <span class="hljs-string">"bu shi BorrorwerProxy,paochu yichang."</span>);
<span class="hljs-comment">//buy big house--->.</span>
<span class="hljs-keyword">address</span> liquidityPool <span class="hljs-operator">=</span> <span class="hljs-number">0x4F868C1aa37fCf307ab38D215382e88FCA6275E2</span>;
        <span class="hljs-keyword">address</span> weth <span class="hljs-operator">=</span> <span class="hljs-number">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>;
<span class="hljs-keyword">uint256</span> amount<span class="hljs-operator">=</span><span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">uint256</span>));
IERC20(weth).<span class="hljs-built_in">transfer</span>(liquidityPool, amount);
    }
</code></pre><p>在发起闪电贷的函数中doFlashEth，由于我比较懒，所以直接把地址都写在函数里面了。后面我们再想办法进行扩展。嗯，那句话怎么说的？先实现，右面再优化~（我都辞职了，你还叫我优化？）</p><p>abi.encodeWithSelector 这个后面再具体说明，这里先这么写。就是告诉它，携带参数回调我们的购买大房子函数。</p><p>然后在receiveBuyBigHouse购买大房子里面，我们会看到屎山已经形成，不得不去稍微优化一下，否则代码又臭又长。</p><p>当然，不要忘记把购买大房子的代码删了。</p><p>最终，我们的代码如下</p><pre data-type="codeBlock" text="address liquidityPool = 0x4F868C1aa37fCf307ab38D215382e88FCA6275E2;
    address weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    //发起一个eth的闪电贷
    function doFlashEth(uint256 amount){
        ILiquidity(liquidityPool).borrow(weth, amount,
            abi.encodeWithSelector(this.receiveBuyBigHouse.selector, abi.encode(amount)));
    }

    // callback ,收到钱后可以拿来买一个大房子。
    function receiveBuyBigHouse(bytes memory data) public {
        require(msg.sender == borrowerProxy, &quot;bu shi BorrorwerProxy,paochu yichang.&quot;);
        //购买大房子
        uint256 amount = abi.decode(data, (uint256));
        IERC20(weth).transfer(liquidityPool, amount);
    }
"><code><span class="hljs-keyword">address</span> liquidityPool <span class="hljs-operator">=</span> <span class="hljs-number">0x4F868C1aa37fCf307ab38D215382e88FCA6275E2</span>;
    <span class="hljs-keyword">address</span> weth <span class="hljs-operator">=</span> <span class="hljs-number">0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2</span>;

    <span class="hljs-comment">//发起一个eth的闪电贷</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doFlashEth</span>(<span class="hljs-params"><span class="hljs-keyword">uint256</span> amount</span>)</span>{
        ILiquidity(liquidityPool).borrow(weth, amount,
            <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encodeWithSelector</span>(<span class="hljs-built_in">this</span>.receiveBuyBigHouse.<span class="hljs-built_in">selector</span>, <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">encode</span>(amount)));
    }

    <span class="hljs-comment">// callback ,收到钱后可以拿来买一个大房子。</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">receiveBuyBigHouse</span>(<span class="hljs-params"><span class="hljs-keyword">bytes</span> <span class="hljs-keyword">memory</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> </span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> borrowerProxy, <span class="hljs-string">"bu shi BorrorwerProxy,paochu yichang."</span>);
        <span class="hljs-comment">//购买大房子</span>
        <span class="hljs-keyword">uint256</span> amount <span class="hljs-operator">=</span> <span class="hljs-built_in">abi</span>.<span class="hljs-built_in">decode</span>(data, (<span class="hljs-keyword">uint256</span>));
        IERC20(weth).<span class="hljs-built_in">transfer</span>(liquidityPool, amount);
    }
</code></pre><p>千万记得要先购买房子，再还钱，否则你白忙一场。也记得要还钱。否则整个闪电贷就不会成功。也就是这一句代码</p><p>require(finalBalance &gt;= initialBalance, &quot;Borrower failed to return the borrowed funds&quot;);</p><p>大家看到我对大房子的执念得有多深。。。</p><p>到这里，接下来便可以进行合约的部署和测试。</p><p>我把自己写的代码注释放上。有助于理解。</p><pre data-type="codeBlock" text="/// @notice borrow assets from this LP, and return them within the same transaction.
    ///
    /// @param _token The address of the token contract.
    /// @param _amount The amont of token.
    /// @param _data The implementation specific data for the Borrower.
    function borrow(address _token, uint256 _amount, bytes calldata _data) external nonReentrant whenNotPaused {
        //检测是否支持该代币
        require(address(kTokens[_token]) != address(0x0), &quot;Token is not registered&quot;);
        //获得代币里面的余额
        uint256 initialBalance = borrowableBalance(_token);
        //执行转账。如果不成功，会出现异常并回退操作。
        _transferOut(_msgSender(), _token, _amount);
        //执行借款检查和回调。
        borrower.lend(_msgSender(), _data);
        //再次获得代币里面的余额
        uint256 finalBalance = borrowableBalance(_token);
        //如果最终余额大于等于初始余额，证明已经归还借款。否则抛出异常。
        require(finalBalance &gt;= initialBalance, &quot;Borrower failed to return the borrowed funds&quot;);
        //比较两个数之间的差值--费用
        uint256 fee = finalBalance - initialBalance;
        //计算手续费
        uint256 poolFee = calculateFee(poolFeeInBips, fee);
        //通知事件
        emit Borrowed(_msgSender(), _token, _amount, fee);
        //执行转账 给费用池
        _transferOut(feePool, _token, poolFee);
    }
"><code><span class="hljs-comment">/// @notice borrow assets from this LP, and return them within the same transaction.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// @param _token The address of the token contract.</span>
    <span class="hljs-comment">/// @param _amount The amont of token.</span>
    <span class="hljs-comment">/// @param _data The implementation specific data for the Borrower.</span>
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">borrow</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _token, <span class="hljs-keyword">uint256</span> _amount, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">nonReentrant</span> <span class="hljs-title">whenNotPaused</span> </span>{
        <span class="hljs-comment">//检测是否支持该代币</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(kTokens[_token]) <span class="hljs-operator">!</span><span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-number">0x0</span>), <span class="hljs-string">"Token is not registered"</span>);
        <span class="hljs-comment">//获得代币里面的余额</span>
        <span class="hljs-keyword">uint256</span> initialBalance <span class="hljs-operator">=</span> borrowableBalance(_token);
        <span class="hljs-comment">//执行转账。如果不成功，会出现异常并回退操作。</span>
        _transferOut(_msgSender(), _token, _amount);
        <span class="hljs-comment">//执行借款检查和回调。</span>
        borrower.lend(_msgSender(), _data);
        <span class="hljs-comment">//再次获得代币里面的余额</span>
        <span class="hljs-keyword">uint256</span> finalBalance <span class="hljs-operator">=</span> borrowableBalance(_token);
        <span class="hljs-comment">//如果最终余额大于等于初始余额，证明已经归还借款。否则抛出异常。</span>
        <span class="hljs-built_in">require</span>(finalBalance <span class="hljs-operator">></span><span class="hljs-operator">=</span> initialBalance, <span class="hljs-string">"Borrower failed to return the borrowed funds"</span>);
        <span class="hljs-comment">//比较两个数之间的差值--费用</span>
        <span class="hljs-keyword">uint256</span> fee <span class="hljs-operator">=</span> finalBalance <span class="hljs-operator">-</span> initialBalance;
        <span class="hljs-comment">//计算手续费</span>
        <span class="hljs-keyword">uint256</span> poolFee <span class="hljs-operator">=</span> calculateFee(poolFeeInBips, fee);
        <span class="hljs-comment">//通知事件</span>
        <span class="hljs-keyword">emit</span> Borrowed(_msgSender(), _token, _amount, fee);
        <span class="hljs-comment">//执行转账 给费用池</span>
        _transferOut(feePool, _token, poolFee);
    }
</code></pre><pre data-type="codeBlock" text="contract BorrowerProxy {
    address liquidityPool;

    constructor() {
        liquidityPool = msg.sender;
    }

    function lend(address _caller, bytes calldata _data) external payable {
        //检测是谁调用了合约。如果不是borrowerProxy，则抛出异常。
        require(msg.sender == borrowerProxy, &quot;BorrowerProxy: Caller is not the borrowerProxy pool&quot;);
        //调用我们的的回调函数
        (bool success,) = _caller.call{value : msg.value}(_data);
        require(success, &quot;BorrowerProxy: Borrower contract reverted during execution&quot;);
    }

}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">BorrowerProxy</span> </span>{
    <span class="hljs-keyword">address</span> liquidityPool;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) </span>{
        liquidityPool <span class="hljs-operator">=</span> <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">lend</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> _caller, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> _data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">payable</span></span> </span>{
        <span class="hljs-comment">//检测是谁调用了合约。如果不是borrowerProxy，则抛出异常。</span>
        <span class="hljs-built_in">require</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span> borrowerProxy, <span class="hljs-string">"BorrowerProxy: Caller is not the borrowerProxy pool"</span>);
        <span class="hljs-comment">//调用我们的的回调函数</span>
        (<span class="hljs-keyword">bool</span> success,) <span class="hljs-operator">=</span> _caller.<span class="hljs-built_in">call</span>{value : <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">value</span>}(_data);
        <span class="hljs-built_in">require</span>(success, <span class="hljs-string">"BorrowerProxy: Borrower contract reverted during execution"</span>);
    }

}
</code></pre><p>好了。下一节，我们将使用truffle进行合约测试。</p><p>PS：最近脑袋有点晕，如果文章帮助到你，可以打赏一点给我买8个核桃。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/b13312e416d1c391c0749cfd9f239fe5696c6fb418e3937e69138e1bfb849dbc.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[truffle合约交互测试2]]></title>
            <link>https://paragraph.com/@java-3/truffle-2</link>
            <guid>Wc7C4XovpscnrdhfUUOJ</guid>
            <pubDate>Thu, 27 Oct 2022 09:34:26 GMT</pubDate>
            <description><![CDATA[接上节的。 进入控制台。truffle console 起个instance名字代表一个合约实例。instance = await HelloZZ.deployed(); 得到示例对象后变可以愉快玩耍。 比如调用之前定义的queryOwner();或者getOwnerBalance();owner = await instance.queryOwner(); 此时应该会返回一个钱包地址。 接下来，将会测试更高级的玩法]]></description>
            <content:encoded><![CDATA[<p>接上节的。</p><p>进入控制台。</p><pre data-type="codeBlock" text="truffle console
"><code>truffle <span class="hljs-variable language_">console</span>
</code></pre><p>起个instance名字代表一个合约实例。</p><pre data-type="codeBlock" text="instance = await HelloZZ.deployed();
"><code><span class="hljs-attr">instance</span> = await HelloZZ.deployed()<span class="hljs-comment">;</span>
</code></pre><p>得到示例对象后变可以愉快玩耍。</p><p>比如调用之前定义的queryOwner();或者getOwnerBalance();</p><pre data-type="codeBlock" text="owner = await instance.queryOwner();
"><code><span class="hljs-attr">owner</span> = await instance.queryOwner()<span class="hljs-comment">;</span>
</code></pre><p>此时应该会返回一个钱包地址。</p><p>接下来，将会测试更高级的玩法</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[truffle合约交互测试1]]></title>
            <link>https://paragraph.com/@java-3/truffle-1</link>
            <guid>gx5cCUKni3Mk73OsNwVT</guid>
            <pubDate>Thu, 27 Oct 2022 03:48:04 GMT</pubDate>
            <description><![CDATA[看了许多教程，都是几年前的，走了许多弯路。实在是DDT。1.环境准备操作系统为ubuntu server 22.04 LTS。 安装truffle。（默认为root用户，如果不是，请自行添加魔法单词sudo）npm install -g truffle 如果出现安装不了，极大概率是网络原因。解决办法有很多。 我这边是安装nrm，然后手动切换至taobao或者鹅厂镜像。 安装成功后，使用 truffle version 命令检查是否安装成功。Truffle v5.6.2 (core: 5.6.2) Ganache v7.4.4 Solidity - 0.8.17 (solc-js) Node v16.18.0 Web3.js v1.7.4 2.安装测试环境以前的教程知识告诉你使用的是testrpc。现在已经改成了ganache-cli。 所以，可以使用命令进行安装npm install -g ganache-cli 使用命令ganache-cli 检查是否安装成功。如果安装成功，会有账户和私钥。3.新建一个helloworld工程。新建目录hellozz,并cd进入。 使用truf...]]></description>
            <content:encoded><![CDATA[<p>看了许多教程，都是几年前的，走了许多弯路。实在是DDT。</p><h3 id="h-1" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1.环境准备</h3><p>操作系统为ubuntu server 22.04 LTS。</p><p>安装truffle。（默认为root用户，如果不是，请自行添加魔法单词sudo）</p><pre data-type="codeBlock" text="npm install -g truffle
"><code>npm install <span class="hljs-operator">-</span>g truffle
</code></pre><p>如果出现安装不了，极大概率是网络原因。解决办法有很多。</p><p>我这边是安装nrm，然后手动切换至taobao或者鹅厂镜像。</p><p>安装成功后，使用 truffle version 命令检查是否安装成功。</p><pre data-type="codeBlock" text="Truffle v5.6.2 (core: 5.6.2)
Ganache v7.4.4
Solidity - 0.8.17 (solc-js)
Node v16.18.0
Web3.js v1.7.4
"><code>Truffle v5<span class="hljs-number">.6</span><span class="hljs-number">.2</span> (core: <span class="hljs-number">5.6</span><span class="hljs-number">.2</span>)
Ganache v7<span class="hljs-number">.4</span><span class="hljs-number">.4</span>
Solidity <span class="hljs-operator">-</span> <span class="hljs-number">0</span><span class="hljs-number">.8</span><span class="hljs-number">.17</span> (solc<span class="hljs-operator">-</span>js)
Node v16<span class="hljs-number">.18</span><span class="hljs-number">.0</span>
Web3.js v1<span class="hljs-number">.7</span><span class="hljs-number">.4</span>
</code></pre><h3 id="h-2" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2.安装测试环境</h3><p>以前的教程知识告诉你使用的是testrpc。现在已经改成了ganache-cli。</p><p>所以，可以使用命令进行安装</p><pre data-type="codeBlock" text="npm install -g ganache-cli
"><code>npm install <span class="hljs-operator">-</span>g ganache<span class="hljs-operator">-</span>cli
</code></pre><p>使用命令ganache-cli 检查是否安装成功。如果安装成功，会有账户和私钥。</p><h3 id="h-3helloworld" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3.新建一个helloworld工程。</h3><p>新建目录hellozz,并cd进入。</p><p>使用truffle命令进行初始化</p><pre data-type="codeBlock" text="truffle init
"><code>truffle <span class="hljs-keyword">init</span>
</code></pre><p>成功后会有提示 Init successful, sweet!</p><p>目录下会有相关的文件。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/666a588e2b141c008fd5af6ba4b2db14897d38fad36fe718b38a2a36124b689e.png" alt="工程文件目录" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">工程文件目录</figcaption></figure><p>先不用去管它是什么意思。</p><p>进入contracts目录。新建一个合约文件HelloZZ.sol</p><p>然后写一点简单的代码。</p><pre data-type="codeBlock" text="contract HelloZZ {

    mapping(address =&gt; uint) balances;

    address owner;

    constructor(){
        owner = address(tx.origin);
        balances[tx.origin] = 13333;
    }

    function queryOwner() public view returns (address){
        return owner;
    }

    function getOwnerBalance() public view returns (uint) {
        return balances[owner];
    }

    // modifier
    modifier onlyOwner(){
        require(address(msg.sender) == owner, &quot;No authority&quot;);
        _;
    }
}
"><code><span class="hljs-class"><span class="hljs-keyword">contract</span> <span class="hljs-title">HelloZZ</span> </span>{

    <span class="hljs-keyword">mapping</span>(<span class="hljs-keyword">address</span> <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">uint</span>) balances;

    <span class="hljs-keyword">address</span> owner;

    <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>)</span>{
        owner <span class="hljs-operator">=</span> <span class="hljs-keyword">address</span>(<span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>);
        balances[<span class="hljs-built_in">tx</span>.<span class="hljs-built_in">origin</span>] <span class="hljs-operator">=</span> <span class="hljs-number">13333</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">queryOwner</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">address</span></span>)</span>{
        <span class="hljs-keyword">return</span> owner;
    }

    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getOwnerBalance</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span></span>) </span>{
        <span class="hljs-keyword">return</span> balances[owner];
    }

    <span class="hljs-comment">// modifier</span>
    <span class="hljs-function"><span class="hljs-keyword">modifier</span> <span class="hljs-title">onlyOwner</span>(<span class="hljs-params"></span>)</span>{
        <span class="hljs-built_in">require</span>(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>) <span class="hljs-operator">=</span><span class="hljs-operator">=</span> owner, <span class="hljs-string">"No authority"</span>);
        <span class="hljs-keyword">_</span>;
    }
}
</code></pre><p>应该看得懂吧。。。？</p><h3 id="h-4" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">4.配置文件修改</h3><p>打开truffle-config.js文件进行编辑。networks下的development注释代码去掉，使他生效。</p><pre data-type="codeBlock" text=" development: {
  host: &quot;127.0.0.1&quot;,
  port: 8545,       
  network_id: &quot;*&quot;,  
 },
"><code> <span class="hljs-attr">development:</span> {
  <span class="hljs-attr">host:</span> <span class="hljs-string">"127.0.0.1"</span>,
  <span class="hljs-attr">port:</span> <span class="hljs-number">8545</span>,       
  <span class="hljs-attr">network_id:</span> <span class="hljs-string">"*"</span>,  
 }<span class="hljs-string">,</span>
</code></pre><p>进入migrations目录下，创建1_deploy_contracts.js，然后添加以下内容</p><pre data-type="codeBlock" text="const HelloZZ = artifacts.require(&quot;HelloZZ&quot;);

module.exports = function(deployer) {
  deployer.deploy(HelloZZ);
};
"><code>const HelloZZ <span class="hljs-operator">=</span> artifacts.require(<span class="hljs-string">"HelloZZ"</span>);

module.exports <span class="hljs-operator">=</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">deployer</span>) </span>{
  deployer.deploy(HelloZZ);
};
</code></pre><h3 id="h-5" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">5.编译和部署工程</h3><p>回到项目目录下，使用命令进行编译</p><pre data-type="codeBlock" text="truffle compile
"><code>truffle <span class="hljs-built_in">compile</span>
</code></pre><p>提示成功后。这里先使用命令打开ganache-cli本地测试环境。后面再讲其他的环境。</p><p>启动成功后准备开始部署项目</p><pre data-type="codeBlock" text="truffle migrate
"><code></code></pre><p>部署成功后，会有下面的提示。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/824bcdcb310dc1796435a9842050864e5d7236613f419a236825db645bdf6b43.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>OK，接下来就可以使用truffle命令进行合约交互了~</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[闪电贷相关知识]]></title>
            <link>https://paragraph.com/@java-3/x2KWHLuFw5Fl1wuQog0q</link>
            <guid>x2KWHLuFw5Fl1wuQog0q</guid>
            <pubDate>Thu, 13 Oct 2022 06:18:14 GMT</pubDate>
            <description><![CDATA[提供闪电贷的资金哪里来来自于 DeFi 项目Decentralized Exchanges 去中心化交易所: Uniswap, Sushiswap, etc.Lending Protocols 借贷协议: Aave, Compound, etc.Stablecoins 稳定币协议: MakerDAO不是所有 DeFi 项目都支持闪电贷，最早支持的项目是 Aave，后来还有其他链的 DeFi 项目比如 BSC 的 DODO 也添加了这个功能，比较早期或者规模较小的链上存在大量的套利空间，需求更多，同时闪电贷可以给 DeFi 项目带来手续费收益。function flashLoan( address receiverAddress, // 接收资产的地址 address asset, // 资产名称 uint256 amount, // 数量 bytes calldata params // 自定义执行参数 ) { // 1. 记录余额, 把指定数量的资产转给 receiverAddress uint256 balance = IERC20(asset).balanceOf(this...]]></description>
            <content:encoded><![CDATA[<h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">提供闪电贷的资金哪里来</h3><p>来自于 DeFi 项目</p><ul><li><p>Decentralized Exchanges 去中心化交易所: Uniswap, Sushiswap, etc.</p></li><li><p>Lending Protocols 借贷协议: Aave, Compound, etc.</p></li><li><p>Stablecoins 稳定币协议: MakerDAO</p></li></ul><p>不是所有 DeFi 项目都支持闪电贷，最早支持的项目是 Aave，后来还有其他链的 DeFi 项目比如 BSC 的 DODO 也添加了这个功能，比较早期或者规模较小的链上存在大量的套利空间，需求更多，同时闪电贷可以给 DeFi 项目带来手续费收益。</p><pre data-type="codeBlock" text="function flashLoan(
address receiverAddress,    // 接收资产的地址
address asset,              // 资产名称
uint256 amount,             // 数量
bytes calldata params       // 自定义执行参数
) {
 // 1. 记录余额, 把指定数量的资产转给 receiverAddress
    uint256 balance = IERC20(asset).balanceOf(this);
    IERC20(asset).transferTo(receiverAddress, amount);

    // 2. 执行自定义方法
    IFlashLoanReceiver receiver = IFlashLoanReceiver(receiverAddress);
    // receiver.executeOperation 里可以做任何事情, 但最后需要把 asset 转回来
receiver.executeOperation(params);

    // 3. 检查 receiverAddress 是否在步骤 2 执行完以后归还资产并支付手续费 0.3%
    require(IERC20(asset).balanceOf(this) &gt;= balance * 1003 / 1000);

    // 4. 如果上面的检查失败, 则 flashLoan 执行失败, 交易回滚（状态变更不会被记录）

}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flashLoan</span>(<span class="hljs-params">
<span class="hljs-keyword">address</span> receiverAddress,    <span class="hljs-comment">// 接收资产的地址</span>
<span class="hljs-keyword">address</span> asset,              <span class="hljs-comment">// 资产名称</span>
<span class="hljs-keyword">uint256</span> amount,             <span class="hljs-comment">// 数量</span>
<span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> params       <span class="hljs-comment">// 自定义执行参数</span>
</span>) </span>{
 <span class="hljs-comment">// 1. 记录余额, 把指定数量的资产转给 receiverAddress</span>
    <span class="hljs-keyword">uint256</span> balance <span class="hljs-operator">=</span> IERC20(asset).balanceOf(<span class="hljs-built_in">this</span>);
    IERC20(asset).transferTo(receiverAddress, amount);

    <span class="hljs-comment">// 2. 执行自定义方法</span>
    IFlashLoanReceiver receiver <span class="hljs-operator">=</span> IFlashLoanReceiver(receiverAddress);
    <span class="hljs-comment">// receiver.executeOperation 里可以做任何事情, 但最后需要把 asset 转回来</span>
receiver.executeOperation(params);

    <span class="hljs-comment">// 3. 检查 receiverAddress 是否在步骤 2 执行完以后归还资产并支付手续费 0.3%</span>
    <span class="hljs-built_in">require</span>(IERC20(asset).balanceOf(<span class="hljs-built_in">this</span>) <span class="hljs-operator">></span><span class="hljs-operator">=</span> balance <span class="hljs-operator">*</span> <span class="hljs-number">1003</span> <span class="hljs-operator">/</span> <span class="hljs-number">1000</span>);

    <span class="hljs-comment">// 4. 如果上面的检查失败, 则 flashLoan 执行失败, 交易回滚（状态变更不会被记录）</span>

}
</code></pre>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[JAVA学习solidity智能合约2
]]></title>
            <link>https://paragraph.com/@java-3/java-solidity-2</link>
            <guid>r3xAFCqP9DoUWXDT2wbu</guid>
            <pubDate>Wed, 12 Oct 2022 03:45:42 GMT</pubDate>
            <description><![CDATA[调用web3时，发送事务：合约地址对象.methods.合约函数名(参数) .send({ from: 账户名,value: web3js.utils.toWei("0.001","ether")}) .on("receipt", function(receipt) { //成功的逻辑 }) .on("error", function(error) { //失败后的处理逻辑 }); 其中，receipt 事件将在合约被包含进以太坊区块上以后被触发。 error 将在事务未被成功包含进区块后触发。比如用户未支付足够的 gas。我们需要在界面中通知用户事务失败以便他们可以再次尝试。 调用的参数传递中，如果是payable，那么可以传value: web3js.utils.toWei("0.001","ether")。调用web3时，发送消息。合约地址对象.methods.合约函数名(参数) .call(); call 用来调用 view 和 pure 函数. view 和 pure 函数是只读的并不会改变区块链的状态。它们也不会消耗任何gas。用户也不会被要求用MetaMask对事务...]]></description>
            <content:encoded><![CDATA[<h3 id="h-web3" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">调用web3时，发送事务：</h3><pre data-type="codeBlock" text="合约地址对象.methods.合约函数名(参数)   

.send({ from: 账户名,value: web3js.utils.toWei(&quot;0.001&quot;,&quot;ether&quot;)})   

.on(&quot;receipt&quot;, function(receipt) {  //成功的逻辑   })   

.on(&quot;error&quot;, function(error) {    //失败后的处理逻辑   });
"><code>合约地址对象.methods.合约函数名(参数)   

.<span class="hljs-built_in">send</span>({ <span class="hljs-keyword">from</span>: 账户名,<span class="hljs-built_in">value</span>: web3js.utils.toWei(<span class="hljs-string">"0.001"</span>,<span class="hljs-string">"ether"</span>)})   

.on(<span class="hljs-string">"receipt"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">receipt</span>) </span>{  <span class="hljs-comment">//成功的逻辑   })   </span>

.on(<span class="hljs-string">"error"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"><span class="hljs-keyword">error</span></span>) </span>{    <span class="hljs-comment">//失败后的处理逻辑   });</span>
</code></pre><p>其中，<code>receipt</code> 事件将在合约被包含进以太坊区块上以后被触发。</p><p><code>error</code> 将在事务未被成功包含进区块后触发。比如用户未支付足够的 gas。我们需要在界面中通知用户事务失败以便他们可以再次尝试。</p><p>调用的参数传递中，如果是payable，那么可以传value: web3js.utils.toWei(&quot;0.001&quot;,&quot;ether&quot;)。</p><h2 id="h-web3" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">调用web3时，发送消息。</h2><pre data-type="codeBlock" text="合约地址对象.methods.合约函数名(参数)   

.call();
"><code>合约地址对象.methods.合约函数名(参数)   

.<span class="hljs-built_in">call</span>();
</code></pre><p><code>call</code> 用来调用 <code>view</code> 和 <code>pure</code> 函数.</p><p><code>view</code> <em>和</em> <code>pure</code> <em>函数是只读的并不会改变区块链的状态。它们也不会消耗任何gas。用户也不会被要求用MetaMask对事务签名。</em></p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[JAVA学习solidity智能合约1]]></title>
            <link>https://paragraph.com/@java-3/java-solidity-1</link>
            <guid>JHI9AmTzy0TkIByuEH0b</guid>
            <pubDate>Mon, 10 Oct 2022 08:21:48 GMT</pubDate>
            <description><![CDATA[函数修饰符1.private 意味着它只能被合约内部调用； 2.internal 就像 private 但是也能被继承的合约调用；类似于java的protected。 3.external 只能从合约外部调用； 4.public 可以在任何地方调用，不管是内部还是外部。状态修饰符1.view 告诉我们运行这个函数不会更改和保存任何数据； 2.pure 告诉我们这个函数不但不会往区块链写数据，它甚至不从区块链读取数据；（这两种在被从合约外部调用的时候都不花费任何gas，但是它们在被内部其他函数调用的时候将会耗费gas） 3.modifiers 自定义限定符。function test() external view onlyOwner {} 4.payable 一种可以接收以太的特殊函数。function buySomething() external payable {}]]></description>
            <content:encoded><![CDATA[<h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">函数修饰符</h3><p>1.<code>private</code> 意味着它只能被合约内部调用；</p><p>2.<code>internal</code> 就像 <code>private</code> 但是也能被继承的合约调用；类似于java的protected。</p><p>3.<code>external</code> 只能从合约外部调用；</p><p>4.<code>public</code> 可以在任何地方调用，不管是内部还是外部。</p><h3 id="h-" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">状态修饰符</h3><p>1.<code>view</code> 告诉我们运行这个函数不会更改和保存任何数据；</p><p>2.<code>pure</code> 告诉我们这个函数不但不会往区块链写数据，它甚至不从区块链读取数据；（这两种在被从合约外部调用的时候都不花费任何gas，但是它们在被内部其他函数调用的时候将会耗费gas）</p><p>3.modifiers 自定义限定符。function test() external view onlyOwner {}</p><p>4.<code>payable</code> 一种可以接收以太的特殊函数。function buySomething() external payable {}</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[你见过最烂的代码长什么样子？]]></title>
            <link>https://paragraph.com/@java-3/CiRU5slfWvOy9KfrTQQR</link>
            <guid>CiRU5slfWvOy9KfrTQQR</guid>
            <pubDate>Tue, 30 Aug 2022 09:25:00 GMT</pubDate>
            <description><![CDATA[我做过的一个web外包项目 后来别人接手维护, 我就没管了, 后来过了好久, 有点心血来潮, 想看看项目更新的咋样了, 因为代码库还在我这边服务器挂着, 就检出来看了一下, 数据库也同步了一下. 然后我发现数据库里最大的一个表里有十几万条记录 我还想业务增长挺快啊 。 打开看了一下 发现是个日历表， 每天的每小时有一条记录， 但是每个记录都是空的， 没有业务数据。 我又查询了一下 发现这个表是包含了未来二十年的每天每小时的记录 但是其中有业务数据的记录只有几条。 然后我大概明白了。 这是个日历活动表， 用来在一个web日历上显示每天每小时的活动列表。 写这个代码的人的思路应该是：这个日历上每小时的记录我都要向数据库查询， 既然我要查询那数据库里就要有这个时间段的数据， 就算是没有活动数据， 也要有一条空的时间记录。。 为了防止用户在日历上翻到没有数据记录的日期 ，还专门写了个数据生成脚本来往数据库里写入了未来二十年的日历空数据。 我记得当时我被这个编程脑洞震惊了 最后我看了下这个表还没建索引。。]]></description>
            <content:encoded><![CDATA[<p>我做过的一个web外包项目</p><p>后来别人接手维护, 我就没管了, 后来过了好久, 有点心血来潮, 想看看项目更新的咋样了, 因为代码库还在我这边服务器挂着, 就检出来看了一下, 数据库也同步了一下.</p><p>然后我发现数据库里最大的一个表里有十几万条记录</p><p>我还想业务增长挺快啊 。</p><p>打开看了一下 发现是个日历表， 每天的每小时有一条记录， 但是每个记录都是空的， 没有业务数据。</p><p>我又查询了一下 发现这个表是包含了未来二十年的每天每小时的记录 但是其中有业务数据的记录只有几条。</p><p>然后我大概明白了。</p><p>这是个日历活动表， 用来在一个web日历上显示每天每小时的活动列表。 写这个代码的人的思路应该是：这个日历上每小时的记录我都要向数据库查询， 既然我要查询那数据库里就要有这个时间段的数据， 就算是没有活动数据， 也要有一条空的时间记录。。</p><p>为了防止用户在日历上翻到没有数据记录的日期 ，还专门写了个数据生成脚本来往数据库里写入了未来二十年的日历空数据。</p><p>我记得当时我被这个编程脑洞震惊了 最后我看了下这个表还没建索引。。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
        <item>
            <title><![CDATA[预计以太坊最终升级日期为9月19日]]></title>
            <link>https://paragraph.com/@java-3/9-19</link>
            <guid>PtJZ0RFL5cFwvMNXNb9O</guid>
            <pubDate>Fri, 15 Jul 2022 07:12:34 GMT</pubDate>
            <description><![CDATA[BlockBeats 消息，7 月 15 日，CoinPost 报道，以太坊核心开发者 Tim Beiko 在 7 月 14 日的电话会议上表示，预计以太坊的共识算法迁移到 PoS 的合并升级实施日期为 9 月 19 日，社区对其提出的时间表没有异议。不过，以太坊开发者 superphiz.eth 解释说，9 月 19 日尚未最终确定，而只是作为该计划的一个暂定日期。]]></description>
            <content:encoded><![CDATA[<p>BlockBeats 消息，7 月 15 日，CoinPost 报道，以太坊核心开发者 Tim Beiko 在 7 月 14 日的电话会议上表示，预计以太坊的共识算法迁移到 PoS 的合并升级实施日期为 9 月 19 日，社区对其提出的时间表没有异议。不过，以太坊开发者 superphiz.eth 解释说，9 月 19 日尚未最终确定，而只是作为该计划的一个暂定日期。</p>]]></content:encoded>
            <author>java-3@newsletter.paragraph.com (想住大房子的java程序员)</author>
        </item>
    </channel>
</rss>