当前位置: 首页 > news >正文

【Solidity】合约交互基础

数据的编码与解码

数据编码:

  1. abi.encode:每个参数都会被填充为 32 字节的数据,并拼接在一起

  2. abi.encodePacked:类似 abi.encode,但会省略其中填充的零

解码:

  1. abi.decode:接受两个参数:编码后的数据和类型列表
contract ABIExample {function encodeData() external pure returns (bytes memory) {uint a = 1;address b = 0x1234567890123456789012345678901234567890;string memory c = "Hello, World!";return abi.encode(a, b, c);}function decodeData(bytes memory data) external pure returns (uint, address, string memory) {(uint a, address b, string memory c) = abi.decode(data,(uint, address, string));return (a, b, c);}
}



函数签名 & 函数选择器

function transfer(address recipient, uint amount) external returns (bool);

函数签名:由函数名称和参数类型组成,中间没有空格 - transfer(address,uint)

函数选择器

  1. 为函数签名通过 Keccak-256 哈希计算得到的前 4 个字节 - bytes4(keccak256("transfer(address,uint)"))
  2. 如果不想手动编写函数签名,可以使用 Solidity 的内置函数 this.functionName.selector 来直接获取函数选择器



函数的编码

  1. abi.encodeWithSignature:第一个参数为函数签名,后面的参数为函数参数

  2. abi.encodeWithSelector:第一个参数为函数选择器,后面的参数为函数参数

contract TargetContract {uint public value;string public message;function setValue(uint _value, string memory _message) public {value = _value;message = _message;}
}contract CallerContract {function callSetValue1(address target,uint _value,string memory _message) public {// 获取函数签名string memory signature = "setValue(uint256,string)";// 通过 encodeWithSignature 编码函数签名及传入的参数(bool success, ) = target.call(abi.encodeWithSignature(signature, _value, _message));require(success, "Call failed");}function callSetValue2(address target,uint _value,string memory _message) public {// 函数选择器 (方法 1)bytes4 selector1 = bytes4(keccak256("setValue(uint256,string)"));// 函数选择器 (方法 2)bytes4 selector2 = TargetContract(target).setValue.selector;// 通过 encodeWithSelector 编码函数选择器及传入的参数(bool success, ) = target.call(abi.encodeWithSelector(selector1, _value, _message));require(success, "Call failed");}
}



直接调用其他合约的方法

contract Demo1 {// 方法 1: 通过地址调用function setDemo2X_1(address _demo2, uint _x) public {Demo2 demo2 = Demo2(_demo2);demo2.setX(_x);}// 方法 2: 通过合约实例调用function setDemo2X_2(Demo2 _demo2, uint _x) public {_demo2.setX(_x);}
}contract Demo2 {uint public x;event Log(address caller, uint x);function setX(uint _x) public {x = _x;emit Log(msg.sender, x);}
}
  1. 部署 Demo2 合约

  2. 调用 Demo2 合约的 setX 方法,设置 x 值;查看 Demo2 合约的 x 值,可以看到 x 值被更新;查看 Log 事件,可以看到调用者地址为编辑器地址

  3. 部署 Demo1 合约

  4. 传入 Demo1 合约的地址和新 x 值,调用 Demo1 合约的 setDemo2X_1 方法;查看 Demo2 合约的 x 值,可以看到 x 值被更新;查看 Log 事件,可以看到调用者地址为 Demo1 合约地址

  5. 传入 Demo1 合约的地址和新 x 值,调用 Demo1 合约的 setDemo2X_2 方法;查看 Demo2 合约的 x 值,可以看到 x 值被更新;查看 Log 事件,可以看到调用者地址为 Demo1 合约地址


可以在调用的同时传输以太币:

contract Demo1 {function setDemo2X(Demo2 _demo2, uint _x) public payable {_demo2.setX{value: msg.value}(_x); // 要求: msg.value >= value 值;  这里设置成一样}
}contract Demo2 {uint public x;uint public value;address public caller;function setX(uint _x) public payable {value = msg.value;caller = msg.sender;x = _x;}
}
  1. 部署 Demo2 合约

  2. 传入新 x 值,设置以太币数量,调用 Demo2 合约的 setX 方法;查看 Demo2 合约的 x 值、value 值、caller 值,可以看到 x 值被更新、value 值为设置的以太币数量、caller 值为编辑器地址

  3. 部署 Demo1 合约

  4. 传入 Demo2 合约的地址、新 x 值,设置以太币数量,调用 Demo1 合约的 setDemo2X 方法;查看 Demo2 合约的 x 值、value 值、caller 值,可以看到 x 值被更新、value 值为设置的以太币数量、caller 值为 Demo1 合约地址



Interface

接口(Interface)用于定义合约之间的交互标准,确保不同合约之间可以互操作。

  1. 接口不能定义状态变量
  2. 接口只声明函数的签名,而不包含函数的实现;所有函数必须声明为 external;不能包含构造函数
  3. 接口可以继承其他接口,但不能继承合约。

现有如下合约交互:

contract Counter {uint public count;function increment() public {count += 1;}
}contract MyContract {// 通过地址调用 Counter 合约的 increment 方法function incrementCounter(address _counter) public {Counter(_counter).increment();}// 通过地址获取 Counter 合约的状态变量 countfunction getCount(address _counter) public view returns (uint) {return Counter(_counter).count();}
}

使用接口:

interface ICounter {function increment() external;function count() external view returns (uint);
}contract MyContract {function incrementCounter(address _counter) public {ICounter(_counter).increment();}function getCount(address _counter) public view returns (uint) {return ICounter(_counter).count();}
}



通过 call 方法调用其他合约的方法

call 是一个比较底层的方法,可以用来调用其他合约的函数 同时发送以太。

contract TestCall {event Log(string _str, uint _num, uint _value, address _sender);function foo(string calldata _str,uint _num) external payable returns (string memory, uint) {emit Log(_str, _num, msg.value ,msg.sender);return (_str, _num);}
}contract Call {bytes public data;function testCall(address _addr) public payable {(bool success, bytes memory _data) = _addr.call{// 传输的以太数量; 若设置的以太数量小于该下限, 会报错value: 100,// gas 上限; 若消耗的 gas 大于该上限, 会报错gas: 500000}(// 传入 encodeWithSignature 包装后的调用数据; 第 1 参数是方法签名, 不能有空格, 不能用简写abi.encodeWithSignature("foo(string,uint256)", "call foo", 123));require(success, "call failed");data = _data; // 返回值 _data 是被调用合约的方法的返回值}
}
  1. 部署 TestCall 合约

  2. 传入字符串和数字,设置以太币数量,调用 TestCall 合约的 foo 方法;查看 Log 事件,可以看到传入的字符串、数字、以太币数量、调用者地址 (为编辑器地址)

  3. 部署 Call 合约

  4. 传入 TestCall 合约的地址,设置以太币数量,调用 Call 合约的 testCall 方法;查看 Call 合约的 data 值,可以看到 TestCall 合约的 foo 方法的返回值 (为 bytes 形式);查看 Log 事件,可以看到传入的字符串、数字、以太币数量、调用者地址 (为 Call 合约地址)



相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • google浏览器chrome用户数据(拓展程序,书签等)丢失问题
  • 多线程(4)——单例模式、阻塞队列、线程池、定时器
  • ATGM332D-F8N低功耗、小尺寸单北斗多频定位导航模块规格书
  • 金九银十秋招大模型岗位攻略来了,已收offer,非常详细收藏我这一篇就够了
  • 你是如何克服编程学习中的挫折感的?
  • 2024下半年软考全国报名时间汇总,附报名费用!
  • 巡检机器人有哪些功能和应用场景
  • ATECLOUD算法维护模块全新上线
  • raft、pow、pos算法(一)
  • 4大免费的AI修复工具,让你的老照片焕然一新
  • 机器学习笔记三-检测异常值
  • wincc报警如何通过短信发送给手机
  • TypeScript学习笔记2---ts的函数定义详解
  • 专利有哪几种类型?
  • PS 笔记
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • 「前端」从UglifyJSPlugin强制开启css压缩探究webpack插件运行机制
  • 【EOS】Cleos基础
  • 【译】理解JavaScript:new 关键字
  • Angular 4.x 动态创建组件
  • Hexo+码云+git快速搭建免费的静态Blog
  • Java 最常见的 200+ 面试题:面试必备
  • java2019面试题北京
  • JavaScript 基础知识 - 入门篇(一)
  • Java基本数据类型之Number
  • Laravel 实践之路: 数据库迁移与数据填充
  • Map集合、散列表、红黑树介绍
  • MySQL主从复制读写分离及奇怪的问题
  • React组件设计模式(一)
  • sessionStorage和localStorage
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 浮现式设计
  • 和 || 运算
  • 基于web的全景—— Pannellum小试
  • 事件委托的小应用
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 树莓派 - 使用须知
  • Java性能优化之JVM GC(垃圾回收机制)
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​用户画像从0到100的构建思路
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • (windows2012共享文件夹和防火墙设置
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (六)Flink 窗口计算
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (十八)三元表达式和列表解析
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • ****Linux下Mysql的安装和配置
  • *上位机的定义
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .Net IE10 _doPostBack 未定义
  • .net MVC中使用angularJs刷新页面数据列表
  • .net 按比例显示图片的缩略图
  • .net 反编译_.net反编译的相关问题
  • .net 托管代码与非托管代码