Michael.W基于Foundry精读Openzeppelin第59期——Proxy.sol

2024-06-24 04:36

本文主要是介绍Michael.W基于Foundry精读Openzeppelin第59期——Proxy.sol,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Michael.W基于Foundry精读Openzeppelin第59期——Proxy.sol

      • 0. 版本
        • 0.1 Proxy.sol
      • 1. 目标合约
      • 2. 代码精读
        • 2.1 _delegate(address implementation) internal
        • 2.2 _implementation() internal && _beforeFallback() internal
        • 2.3 fallback() && receive()

0. 版本

[openzeppelin]:v4.8.3,[forge-std]:v1.5.6

0.1 Proxy.sol

Github: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/proxy/Proxy.sol

Proxy库对外只暴露了fallback和receive函数,是代理合约的基础实现。所有对Proxy合约的call都将被delegatecall到implement合约并且delegatecall的执行结果会原封不动地返还给Proxy合约的调用方。我们通常称implement合约为代理合约背后的逻辑合约。

1. 目标合约

继承Proxy合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/src/proxy/MockProxy.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;import "openzeppelin-contracts/contracts/proxy/Proxy.sol";contract MockProxy is Proxy {address immutable private _IMPLEMENTATION_ADDR;bool immutable private _ENABLE_BEFORE_FALLBACK;event ProxyBeforeFallback(uint value);constructor(address implementationAddress,bool enableBeforeFallback){_IMPLEMENTATION_ADDR = implementationAddress;_ENABLE_BEFORE_FALLBACK = enableBeforeFallback;}function _implementation() internal view override returns (address){return _IMPLEMENTATION_ADDR;}function _beforeFallback() internal override {if (_ENABLE_BEFORE_FALLBACK) {emit ProxyBeforeFallback(msg.value);}}
}

全部foundry测试合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/proxy/Proxy/Proxy.t.sol

测试使用的物料合约:

Github: https://github.com/RevelationOfTuring/foundry-openzeppelin-contracts/blob/master/test/proxy/Proxy/Implement.sol

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;contract Implement {uint public i;address public addr;uint[3] public fixedArray;uint[] public dynamicArray;mapping(uint => uint) public map;event ImplementReceive(uint value);event ImplementFallback(uint value);function setUint(uint target) external {i = target;}function setUintPayable(uint target) external payable {i = target;}function setAddress(address target) external {addr = target;}function setAddressPayable(address target) external payable {addr = target;}function setFixedArray(uint[3] memory target) external {fixedArray = target;}function setFixedArrayPayable(uint[3] memory target) external payable {fixedArray = target;}function setDynamicArray(uint[] memory target) external {dynamicArray = target;}function setDynamicArrayPayable(uint[] memory target) external payable {dynamicArray = target;}function setMapping(uint key, uint value) external {map[key] = value;}function setMappingPayable(uint key, uint value) external payable {map[key] = value;}function triggerRevert() external pure {revert("Implement: revert");}function triggerRevertPayable() external payable {revert("Implement: revert");}function getPure() external pure returns (string memory){return "pure return value";}receive() external payable {emit ImplementReceive(msg.value);}fallback() external payable {emit ImplementFallback(msg.value);}
}

2. 代码精读

2.1 _delegate(address implementation) internal

将当前的call,委托调用到implementation地址。

注:通过内联汇编“黑魔法”,使得没有返回值的_delegate()函数可以动态返回delegatecall的返回值。

    function _delegate(address implementation) internal virtual {// 内联汇编assembly {// 从当前calldata的position 0开始将全部calldata都复制到内存中。内存中的数据存储也是从位置0开始。// 为何此处使用内存的起始position不是从0x40处取空闲内存指针?原因见后文。calldatacopy(0, 0, calldatasize())// 使用delegatecall去调用逻辑合约。// 第一个参数:调用delegatecall的过程允许使用的gas上限。为gas(),即执行到此处剩余可用的全部gas;// 第二个参数:逻辑合约的地址;// 第三个参数:delegatecall所携带的calldata相关。calldata是从当前内存中获取,第三个参数为开始载入的内存position;// 第四个参数:delegatecall所携带的calldata相关。第四个参数为从内存中读取calldata的字节长度;// 综上可知,delegatecall所用的calldata就是进入_delegate(address implementation)时的calldata;// 第五个参数:delegatecall得到的返回数据存储在内存中,第五个参数为开始存储返回值的内存position;// 第六个参数:delegatecall得到的返回数据存储在内存中的字节长度。// 注:由于第五和第六个参数都设为0,即用来存储返回数据的内存长度为0。很明显delegatecall的返回数据长度(如有)要大于设定的存储空间,// 此时,全部的返回数据都要用returndatacopy()来复制到内存中。具体细则详见:https://learnblockchain.cn/article/6309let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)// 由于delegatecall()时设定的存储返回数据的空间为0,要用returndatacopy()和returndatasize()来获取全部的返回数据。// 第一个参数:内存中存储返回数据的起始position,即从position 0处开始存储;// 第二个参数:返回数据被复制的起始position,即从头开始复制返回数据;// 第三个参数:复制返回数据的字节长度。returndatasize()表示未存储到delegatecall()时设定的存储空间的返回数据字节长度,此时该// 值应该为全部返回数据字节长度。// 综上可知,delegatecall()得到的全部返回数据都存储到从0开始的内存空间中returndatacopy(0, 0, returndatasize())// 判断delegatecall是否成功调用switch resultcase 0 {// 如果delegatecall调用失败(例如gas不足),result为0// 那么就直接revert,revert携带的数据为内存中存储的delegatecall的全部返回数据revert(0, returndatasize())}default {// 如果非0(即1),表示delegatecall调用成功// 那么就进行函数返回,返回值为内存中存储的delegatecall的全部返回数据return(0, returndatasize())}}}

为何_delegate()中使用内存的起始position不是从0x40处取空闲内存指针,而是直接从position 0开始?

答:因为在该内联汇编代码块结束时直接进行函数返回,不会再有回到solidity代码逻辑的地方。全部内存都只供汇编代码块使用。只要在内联汇编中手动管理好内存指针,内存就是安全的。

2.2 _implementation() internal && _beforeFallback() internal
  • _implementation():返回逻辑合约的地址。该函数未带实现体,需要在主合约中进行重写;
  • _beforeFallback():执行delegatecall之前会执行的hook函数,如果有需要可以重写该函数并在其中增添逻辑。
    function _implementation() internal view virtual returns (address);function _beforeFallback() internal virtual {}

foundry代码验证:

contract ProxyTest is Test {Implement private _implement = new Implement();address payable private _testingAddress = payable(address(new MockProxy(address(_implement), false)));event ImplementFallback(uint value);event ImplementReceive(uint value);event ProxyBeforeFallback(uint value);function test_beforeFallback() external {_testingAddress = payable(address(new MockProxy(address(_implement), true)));Implement proxy = Implement(_testingAddress);uint proxyBalance = _testingAddress.balance;assertEq(proxyBalance, 0);uint ethValue = 1 wei;// case 1: test setUint()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);proxy.setUint(1024);// case 2:test setUintPayable()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);proxy.setUintPayable{value: ethValue}(1024);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 3: test setAddress()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);proxy.setAddress(address(1));// case 4: test setAddressPayable()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);proxy.setAddressPayable{value: ethValue}(address(1));assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 5: test setFixedArray()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);uint[3] memory targetFixedArray = [uint(1024), 2048, 4096];proxy.setFixedArray(targetFixedArray);// case 6: test setFixedArrayPayable()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);proxy.setFixedArrayPayable{value: ethValue}(targetFixedArray);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 7: test setDynamicArray()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);// build dynamic array as inputuint[] memory targetDynamicArray = new uint[](3);targetDynamicArray[0] = 1024;targetDynamicArray[1] = 2048;targetDynamicArray[2] = 4096;proxy.setDynamicArray(targetDynamicArray);// case 8: test setDynamicArrayPayable()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);proxy.setDynamicArrayPayable{value: ethValue}(targetDynamicArray);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 9: test setMapping()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);proxy.setMapping(1024, 2048);// case 10: test setMapping()vm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);proxy.setMappingPayable{value: ethValue}(1024, 2048);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 11: revert with any static call because it emits event in _beforeFallback()// and causes the evm error: "StateChangeDuringStaticCall"vm.expectRevert();proxy.i();vm.expectRevert();proxy.addr();vm.expectRevert();proxy.fixedArray(0);vm.expectRevert();proxy.dynamicArray(0);vm.expectRevert();proxy.map(1024);vm.expectRevert();proxy.triggerRevert();vm.expectRevert();proxy.getPure();// case 12: revert in the function of implement during a callvm.expectRevert("Implement: revert");proxy.triggerRevertPayable{value: ethValue}();// case 13: call the function not exists in the implement// and delegate call to the fallback function of implement// without valuevm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);emit ImplementFallback(0);bytes memory calldata_ = abi.encodeWithSignature("unknown()");(bool ok,) = _testingAddress.call(calldata_);assertTrue(ok);// with valuevm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);emit ImplementFallback(ethValue);(ok,) = _testingAddress.call{value: ethValue}(calldata_);assertTrue(ok);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 14: call the proxy with empty call data// and delegate call to the receive function of implement// without valuevm.expectEmit(_testingAddress);emit ProxyBeforeFallback(0);emit ImplementReceive(0);(ok,) = _testingAddress.call("");assertTrue(ok);// with valuevm.expectEmit(_testingAddress);emit ProxyBeforeFallback(ethValue);emit ImplementReceive(ethValue);(ok,) = _testingAddress.call{value: ethValue}("");assertTrue(ok);assertEq(_testingAddress.balance, proxyBalance + ethValue);}
}
2.3 fallback() && receive()
  • fallback():当本合约被携带calldata的call调用时,进入该函数。随即将该call的calldata直接delegatecall到逻辑合约;
  • receive():当本合约被不携带任何calldata的call调用时,进入该函数。随即直接delegatecall到逻辑合约(不携带任何calldata)。
    fallback() external payable virtual {// 调用_fallback()_fallback();}receive() external payable virtual {// 调用_fallback()_fallback();}// 携带当前对本合约的call的calldata,delegatecall到逻辑合约function _fallback() internal virtual {// delegatecall之前运行hook函数_beforeFallback();// 携带当前对本合约的call的calldata,delegatecall到逻辑合约_delegate(_implementation());}

foundry代码验证:

contract ProxyTest is Test {Implement private _implement = new Implement();address payable private _testingAddress = payable(address(new MockProxy(address(_implement), false)));event ImplementFallback(uint value);event ImplementReceive(uint value);function test_Call() external {Implement proxy = Implement(_testingAddress);// case 1: set uint256assertEq(proxy.i(), 0);assertEq(_implement.i(), 0);proxy.setUint(1024);// check storage by static callassertEq(proxy.i(), 1024);assertEq(_implement.i(), 0);// check storage by slot numberbytes32 slotNumber = bytes32(uint(0));assertEq(vm.load(_testingAddress, slotNumber), bytes32(uint(1024)));// case 2: set addressassertEq(proxy.addr(), address(0));assertEq(_implement.addr(), address(0));proxy.setAddress(address(2048));// check storage by static callassertEq(proxy.addr(), address(2048));assertEq(_implement.addr(), address(0));// check storage by slot numberslotNumber = bytes32(uint(1));assertEq(vm.load(_testingAddress, slotNumber), bytes32(uint(2048)));// case 3: set fixed arrayassertEq(proxy.fixedArray(0), 0);assertEq(_implement.fixedArray(0), 0);uint[3] memory targetFixedArray = [uint(1024), 2048, 4096];proxy.setFixedArray(targetFixedArray);for (uint i; i < 3; ++i) {// check storage by static callassertEq(proxy.fixedArray(i), targetFixedArray[i]);assertEq(_implement.fixedArray(i), 0);// check storage by slot numberslotNumber = bytes32(uint(2 + i));assertEq(vm.load(_testingAddress, slotNumber), bytes32(targetFixedArray[i]));}// case 4: set dynamic array// revert during static call because dynamic array isn't initializedvm.expectRevert();proxy.dynamicArray(0);vm.expectRevert();_implement.dynamicArray(0);// build dynamic array as inputuint[] memory targetDynamicArray = new uint[](3);targetDynamicArray[0] = 1024;targetDynamicArray[1] = 2048;targetDynamicArray[2] = 4096;proxy.setDynamicArray(targetDynamicArray);for (uint i; i < 3; ++i) {// check storage by static callassertEq(proxy.dynamicArray(i), targetDynamicArray[i]);vm.expectRevert();assertEq(_implement.dynamicArray(i), 0);// check storage by slot numberslotNumber = bytes32(uint(keccak256(abi.encodePacked(uint(5)))) + i);assertEq(vm.load(_testingAddress, slotNumber), bytes32(targetDynamicArray[i]));}// case 5: set mappinguint key = 1024;uint value = 2048;assertEq(proxy.map(key), 0);assertEq(_implement.map(key), 0);proxy.setMapping(key, value);// check storage by static callassertEq(proxy.map(key), value);assertEq(_implement.map(key), 0);// check storage by slot numberslotNumber = bytes32(uint(keccak256(abi.encodePacked(key, uint(6)))));assertEq(vm.load(_testingAddress, slotNumber), bytes32(value));// case 6: revert with msgvm.expectRevert("Implement: revert");proxy.triggerRevert();// case 7: call pure (staticcall)assertEq(proxy.getPure(), "pure return value");// case 8: call the function not exists in the implement// and delegate call to the fallback function of implementvm.expectEmit(_testingAddress);emit ImplementFallback(0);bytes memory calldata_ = abi.encodeWithSignature("unknown()");(bool ok,) = _testingAddress.call(calldata_);assertTrue(ok);// case 9: call without value and calldata// and delegate call to the receive function of implementvm.expectEmit(_testingAddress);emit ImplementReceive(0);(ok,) = _testingAddress.call("");assertTrue(ok);}function test_PayableCall() external {Implement proxy = Implement(_testingAddress);uint proxyBalance = _testingAddress.balance;assertEq(proxyBalance, 0);// case 1: set uint256 payableassertEq(proxy.i(), 0);assertEq(_implement.i(), 0);uint ethValue = 1 wei;proxy.setUintPayable{value: ethValue}(1024);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// check storage by static callassertEq(proxy.i(), 1024);assertEq(_implement.i(), 0);// check storage by slot numberbytes32 slotNumber = bytes32(uint(0));assertEq(vm.load(_testingAddress, slotNumber), bytes32(uint(1024)));// case 2: set address paybleassertEq(proxy.addr(), address(0));assertEq(_implement.addr(), address(0));proxy.setAddressPayable{value: ethValue}(address(2048));assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// check storage by static callassertEq(proxy.addr(), address(2048));assertEq(_implement.addr(), address(0));// check storage by slot numberslotNumber = bytes32(uint(1));assertEq(vm.load(_testingAddress, slotNumber), bytes32(uint(2048)));// case 3: set fixed array payableassertEq(proxy.fixedArray(0), 0);assertEq(_implement.fixedArray(0), 0);uint[3] memory targetFixedArray = [uint(1024), 2048, 4096];proxy.setFixedArrayPayable{value: ethValue}(targetFixedArray);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;for (uint i; i < 3; ++i) {// check storage by static callassertEq(proxy.fixedArray(i), targetFixedArray[i]);assertEq(_implement.fixedArray(i), 0);// check storage by slot numberslotNumber = bytes32(uint(2 + i));assertEq(vm.load(_testingAddress, slotNumber), bytes32(targetFixedArray[i]));}// case 4: set dynamic array payable// revert during static call because dynamic array isn't initializedvm.expectRevert();proxy.dynamicArray(0);vm.expectRevert();_implement.dynamicArray(0);// build dynamic array as inputuint[] memory targetDynamicArray = new uint[](3);targetDynamicArray[0] = 1024;targetDynamicArray[1] = 2048;targetDynamicArray[2] = 4096;proxy.setDynamicArrayPayable{value: ethValue}(targetDynamicArray);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;for (uint i; i < 3; ++i) {// check storage by static callassertEq(proxy.dynamicArray(i), targetDynamicArray[i]);vm.expectRevert();assertEq(_implement.dynamicArray(i), 0);// check storage by slot numberslotNumber = bytes32(uint(keccak256(abi.encodePacked(uint(5)))) + i);assertEq(vm.load(_testingAddress, slotNumber), bytes32(targetDynamicArray[i]));}// case 5: set mapping payableuint key = 1024;uint value = 2048;assertEq(proxy.map(key), 0);assertEq(_implement.map(key), 0);proxy.setMappingPayable{value: ethValue}(key, value);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// check storage by static callassertEq(proxy.map(key), value);assertEq(_implement.map(key), 0);// check storage by slot numberslotNumber = bytes32(uint(keccak256(abi.encodePacked(key, uint(6)))));assertEq(vm.load(_testingAddress, slotNumber), bytes32(value));// case 6: revert with msg payablevm.expectRevert("Implement: revert");proxy.triggerRevertPayable{value: ethValue}();// case 7: call the function not exists in the implement with value// and delegate call to the fallback function of implementvm.expectEmit(_testingAddress);emit ImplementFallback(ethValue);bytes memory calldata_ = abi.encodeWithSignature("unknown()");(bool ok,) = _testingAddress.call{value: ethValue}(calldata_);assertTrue(ok);assertEq(_testingAddress.balance, proxyBalance + ethValue);proxyBalance += ethValue;// case 8: call with value and empty callata// and delegate call to the receive function of implementvm.expectEmit(_testingAddress);emit ImplementReceive(ethValue);(ok,) = _testingAddress.call{value: ethValue}("");assertTrue(ok);assertEq(_testingAddress.balance, proxyBalance + ethValue);}
}

ps:
本人热爱图灵,热爱中本聪,热爱V神。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!

在这里插入图片描述

公众号名称:后现代泼痞浪漫主义奠基人

这篇关于Michael.W基于Foundry精读Openzeppelin第59期——Proxy.sol的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089182

相关文章

SpringBoot集成SOL链的详细过程

《SpringBoot集成SOL链的详细过程》Solanaj是一个用于与Solana区块链交互的Java库,它为Java开发者提供了一套功能丰富的API,使得在Java环境中可以轻松构建与Solana... 目录一、什么是solanaj?二、Pom依赖三、主要类3.1 RpcClient3.2 Public

BERT 论文逐段精读【论文精读】

BERT: 近 3 年 NLP 最火 CV: 大数据集上的训练好的 NN 模型,提升 CV 任务的性能 —— ImageNet 的 CNN 模型 NLP: BERT 简化了 NLP 任务的训练,提升了 NLP 任务的性能 BERT 如何站在巨人的肩膀上的?使用了哪些 NLP 已有的技术和思想?哪些是 BERT 的创新? 1标题 + 作者 BERT: Pre-trainin

proxy代理解决vue中跨域问题

vue.config.js module.exports = {...// webpack-dev-server 相关配置devServer: {host: '0.0.0.0',port: port,open: true,proxy: {'/api': {target: `https://vfadmin.insistence.tech/prod-api`,changeOrigin: true,p

论文精读-Supervised Raw Video Denoising with a Benchmark Dataset on Dynamic Scenes

论文精读-Supervised Raw Video Denoising with a Benchmark Dataset on Dynamic Scenes 优势 1、构建了一个用于监督原始视频去噪的基准数据集。为了多次捕捉瞬间,我们手动为对象s创建运动。在高ISO模式下捕获每一时刻的噪声帧,并通过对多个噪声帧进行平均得到相应的干净帧。 2、有效的原始视频去噪网络(RViDeNet),通过探

天然药物化学史话:“四大光谱”在天然产物结构鉴定中的应用-文献精读46

天然药物化学史话:“四大光谱”在天然产物结构鉴定中的应用,天然产物化学及其生物合成必备基础知识~ 摘要 天然产物化学研究在药物研发中起着非常重要的作用,结构研究又是天然产物化学研究中最重要的工作之一。在天然药物化学史话系列文章的基础上,对在天然产物结构研究中起绝对主导作用的“四大光谱”分析技术,即红外光谱、紫外光谱、质谱、核磁共振波谱在天然产物结构鉴定中的应用历史进行回顾与总结,并对其发展

Spark Core源码精读计划7 | Spark执行环境的初始化

推荐阅读 《Spark源码精度计划 | SparkConf》 《Spark Core源码精读计划 | SparkContext组件初始化》 《Spark Core源码精读计划3 | SparkContext辅助属性及后初始化》 《Spark Core源码精读计划4 | SparkContext提供的其他功能》 《Spark Core源码精读计划5 | 事件总线及ListenerBus》 《Spa

Spark Core源码精读计划3 | SparkContext辅助属性及后初始化

推荐阅读 《关于MQ面试的几件小事 | 消息队列的用途、优缺点、技术选型》         《关于MQ面试的几件小事 | 如何保证消息队列高可用和幂等》 《关于MQ面试的几件小事 | 如何保证消息不丢失》 《关于MQ面试的几件小事 | 如何保证消息按顺序执行》 《关于MQ面试的几件小事 | 消息积压在消息队列里怎么办》 《关于Redis的几件小事 | 使用目的与问题及线程模型》 《关于Red

【上】java获取requestMapping上所有注解功能实现及取匿名注释类的值及 class com.sun.proxy.$Proxy140 转换出错

java获取requestMapping上所有注解功能实现及取匿名注释类的值及 class com.sun.proxy.$Proxy140 转换出错 1,多人相当然以为类似对象一样直接强转下就可以,结果迎来的是class com.sun.proxy.$Proxy140转换出错【想法很勇敢,现实很骨感】 //Class<A> operatorMappingAnnotationType// 错误

Nginx location 和 proxy_pass 配置详解

概述 Nginx 配置中 location 和 proxy_pass 指令的不同组合方式及其对请求转发路径的影响。 配置效果 1. location 和 proxy_pass 都带斜杠 / location /api/ {proxy_pass http://127.0.0.1:8080/;} 访问地址:www.hw.com/api/upload转发地址:http://127.0.0.

qmt量化交易策略小白学习笔记第59期【qmt编程之期权数据--获取指定期权品种的详细信息--原生Python】

qmt编程之获取期权数据 qmt更加详细的教程方法,会持续慢慢梳理。 也可找寻博主的历史文章,搜索关键词查看解决方案 ! 基于BS模型计算欧式期权理论价格 基于Black-Scholes-Merton模型,输入期权标的价格、期权行权价、无风险利率、期权标的年化波动率、剩余天数、标的分红率、计算期权的理论价格 方法1:内置python 调用方法 内置python #encodin