1.pragma solidity ^0.4.0;//^表示版本向上兼容
2. //放置合约
3.contract helloworld{
4. string hello = "Hello World";//字符串
5. //怎么描述对象的行为,function
6. function getName() public view returns(string){
7. return hello;
8. }
9. function ChangeName(string _newName) public
10. {
11. hello = _newName;
12. }
13. function pureTest(string _name) pure public returns(string){
14. return _name;
15. }
16.}
1.2智能合约文件基本组成:
图一 : 智能合约文件组成
完整的合约结构:
1.pragma solidity ^0.4.0;
2.
3.import"";
4.
5.contract Name{
6.
7.}
1.2.1版本声明
其中,^表示版本向上兼容
1.pragam solidity ^版本;
2.pragma solidity ^0.4.0;
1.2.2合约的引入Import
1.import “文件名.sol”
图2:合约引入的示意图
在solidity1.sol合约当中引入了合约solidity_for_import.sol(在图2中红色部分标注)。
1.2.3合约
[引入状态变量、函数、事件、结构体、函数修改器]
1.pragma solidity ^0.4.0;
2.
3.import "solidity_for_import.sol";//引入另外一个合约
4.
5./*this is a Contract
6.@auth:Linghu
7.*/
8.contract Test{
9.
10. //引入状态变量
11. uint a;
12.
13. //引入函数
14. function setA(uint x) public {
15. a=x;
16.
17. //调用setA时触发事件Set_A
18. emit Set_A(x);//利用web3随时监听我们的事件
19. }
20.
21. //引入事件
22. event Set_A(uint a);
23.
24.
25.
26. //定义结构体
27. struct Position{
28. int lat;
29. int lng;
30. }
31.
32. address public ownerAddr;
33. //定义函数修改器
34. modifier ownerAddr(){
35. require(msg.sender==ownerAddr);
36. _;
37. }
38. function mine() public owner {
39. a+=1;
40. }
41.
42. }
1、状态变量
状态变量是永久地存储在合约存储中的值。
1.pragma solidity ^0.4.0;
2.
3.impragma solidity ^0.4.0;
4.
5.contract SimpleStorage {
6. uint storedData; // 状态变量
7. // ...
8.}
2、函数
1.pragma solidity ^0.4.0;
2.
3.contract SimpleAuction {
4. function bid() public payable { // 函数
5. // ...
6. }
7.}
函数是合约中代码的可执行单元
函数调用可发生在合约的内部或者外部,并且函数对其他合约有不同程度的可见性。
内部函数调用
当前合约中的函数可以直接(“从内部”)调用,也可以递归调用,就像下边这个例子一样:
1.pragma solidity ^0.4.16;
2.
3.contract C {
4. function g(uint a) public pure returns (uint ret) { return f(); }
5. function f() internal pure returns (uint ret) { return g(7) + f(); }
6.}
这些函数调用在EVM中被解释为简单的跳转。这样做的效果就是当前内存不会被清除,也就是说,通过内部调用在函数之间传递内存引用是非常有效的。
外部函数调用
表达式this.g(8);和c.g(2);(其中c是合约实例)也是有效的函数调用,但是这种情况下,函数将会通过一个消息调用来被“外部调用”,而不是直接的跳转。请注意,不可以在构造函数中通过this来调用函数,因为此时真实的合约实例还没有被创建。
如果想要调用其他合约的函数,需要外部调用。对于一个外部调用,所有的函数参数都需要被复制到内存。
当调用其他合约的函数时,随函数调用发送的Wei和gas的数量可以分别由特定选项.value()和.gas()指定:
1.pragma solidity ^0.4.0;
2.
3.contract InfoFeed {
4. function info() public payable returns (uint ret) { return 42; }
5.}
6.
7.contract Consumer {
8. InfoFeed feed;
9. function setFeed(address addr) public { feed = InfoFeed(addr); }
10. function callFeed() public { feed.info.value(10).gas(800)(); }
11.}
payable修饰符要用于修饰info,否则,.value()选项将不可用。
注意:表达式InfoFeed(addr)进行了一个的显式类型转换,说明”我们知道给定地址的合约类型是InfoFeed“并且这不会执行构造函数。显式类型转换需要谨慎处理。绝对不要在一个你不清楚类型的合约上执行函数调用。
我们也可以直接使用functionsetFeed(InfoFeed_feed){feed=_feed;}。注意一个事实,feed.info.value(10).gas(800)只(局部地)设置了与函数调用一起发送的Wei值和gas的数量,只有最后的圆括号执行了真正的调用。
如果被调函数所在合约不存在(也就是账户中不包含代码)或者被调用合约本身抛出异常或者gas用完等,函数调用会抛出异常。
注:任何与其他合约的交互都会强加潜在危险,尤其是在不能预先知道合约代码的情况下。当前合约将控制权移交给被调用合约,而被调用合约可能做任何事。即使被调用合约从一个已知父合约继承,继承的合约也只需要有一个正确的接口就可以了。被调用合约的实现可以完全任意,因此会带来危险。此外,请小心万一它再调用你系统中的其他合约,或者甚至在第一次调用返回之前返回到你的调用合约。这意味着被调用合约可以通过它自己的函数改变调用合约的状态变量。。一个建议的函数写法是,例如,在你合约中状态变量进行各种变化后再调用外部函数,这样,你的合约就不会轻易被滥用的重入(reentrancy)所影响
3、结构类型
结构是可以将几个变量分组的自定义类型。
1.pragma solidity ^0.4.0;
2.
3.contract Ballot {
4. struct Voter { // 结构
5. uint weight;
6. bool voted;
7. address delegate;
8. uint vote;
9. }
10.}
4、枚举类型
枚举可用来创建由一定数量的“常量值”构成的自定义类型。
1.pragma solidity ^0.4.0;
2.
3.contract Purchase {
4. enum State { Created, Locked, Inactive } // 枚举
5.}
5、事件
事件是能方便地调用以太坊虚拟机日志功能的接口。
1.pragma solidity ^0.4.21;
2.contract SimpleAuction {
3. event HighestBidIncreased(address bidder, uint amount); // 事件
4.
5. function bid() public payable {
6. // ...
7. emit HighestBidIncreased(msg.sender, msg.value); // 触发事件
8. }
9.}
事件允许我们方便地使用EVM的日志基础设施。我们可以在dapp的用户界面中监听事件,EVM的日志机制可以反过来“调用”用来监听事件的Javascript回调函数。
事件在合约中可被继承。当他们被调用时,会使参数被存储到交易的日志中——一种区块链中的特殊数据结构。这些日志与地址相关联,被并入区块链中,只要区块可以访问就一直存在(在Frontier和Homestead版本中会被永久保存,在Serenity版本中可能会改动)。日志和事件在合约内不可直接被访问(甚至是创建日志的合约也不能访问)。
对日志的SPV(SimplifiedPaymentVerification)证明是可能的,如果一个外部实体提供了一个带有这种证明的合约,它可以检查日志是否真实存在于区块链中。但需要留意的是,由于合约中仅能访问最近的256个区块哈希,所以还需要提供区块头信息。
最多三个参数可以接收indexed属性,从而使它们可以被搜索:在用户界面上可以使用indexed参数的特定值来进行过滤。
如果数组(包括string和bytes)类型被标记为索引项,则它们的keccak-256哈希值会被作为topic保存。
除非你用anonymous说明符声明事件,否则事件签名的哈希值是topic之一。同时也意味着对于匿名事件无法通过名字来过滤。
所有非索引参数都将存储在日志的数据部分中。
注:索引参数本身不会被保存。你只能搜索它们的值(来确定相应的日志数据是否存在),而不能获取它们的值本身。
1.pragma solidity ^0.4.0;
2.
3.contract ClientReceipt {
4. event Deposit(
5. address indexed _from,
6. bytes32 indexed _id,
7. uint _value
8. );
9.
10. function deposit(bytes32 _id) public payable {
11. // 我们可以过滤对 `Deposit` 的调用,从而用 Javascript API 来查明对这个函数的任何调用(甚至是深度嵌套调用)。
12. Deposit(msg.sender, _id, msg.value);
13. }
14.}
使用JavaScriptAPI调用事件的用法如下:
1.var abi = /* abi 由编译器产生 */;
2.var ClientReceipt = web3.eth.contract(abi);
3.var clientReceipt = ClientReceipt.at("0x1234...ab67" /* 地址 */);
4.
5.var event = clientReceipt.Deposit();
6.
7.// 监视变化
8.event.watch(function(error, result){
9. // 结果包括对 `Deposit` 的调用参数在内的各种信息。
10. if (!error)
11. console.log(result);
12.});
13.
14.// 或者通过回调立即开始观察
15.var event = clientReceipt.Deposit(function(error, result) {
16. if (!error)
17. console.log(result);
18.});
6、函数修改器
函数修饰器可以用来以声明的方式改良函数语义。使用修饰器modifier可以轻松改变函数的行为。例如,它们可以在执行函数之前自动检查某个条件。修饰器modifier是合约的可继承属性,并可能被派生合约覆盖。
1.pragma solidity ^0.4.11;
2.
3.contract owned {
4. function owned() public { owner = msg.sender; }
5. address owner;
6.
7. // 这个合约只定义一个修饰器,但并未使用: 它将会在派生合约中用到。
8. // 修饰器所修饰的函数体会被插入到特殊符号 _; 的位置。
9. // 这意味着如果是 owner 调用这个函数,则函数会被执行,否则会抛出异常。
10. modifier onlyOwner {
11. require(msg.sender == owner);
12. _;
13. }
14.}
15.
16.contract mortal is owned {
17. // 这个合约从 `owned` 继承了 `onlyOwner` 修饰符,并将其应用于 `close` 函数,
18. // 只有在合约里保存的 owner 调用 `close` 函数,才会生效。
19. function close() public onlyOwner {
20. selfdestruct(owner);
21. }
22.}
23.
24.contract priced {
25. // 修改器可以接收参数:
26. modifier costs(uint price) {
27. if (msg.value >= price) {
28. _;
29. }
30. }
31.}
32.
33.contract Register is priced, owned {
34. mapping (address => bool) registeredAddresses;
35. uint price;
36.
37. function Register(uint initialPrice) public { price = initialPrice; }
38.
39. // 在这里也使用关键字 `payable` 非常重要,否则函数会自动拒绝所有发送给它的以太币。
40. function register() public payable costs(price) {
41. registeredAddresses[msg.sender] = true;
42. }
43.
44. function changePrice(uint _price) public onlyOwner {
45. price = _price;
46. }
47.}
48.
49.contract Mutex {
50. bool locked;
51. modifier noReentrancy() {
52. require(!locked);
53. locked = true;
54. _;
55. locked = false;
56. }
57.
58. // 这个函数受互斥量保护,这意味着 `msg.sender.call` 中的重入调用不能再次调用 `f`。
59. // `return 7` 语句指定返回值为 7,但修改器中的语句 `locked = false` 仍会执行。
60. function f() public noReentrancy returns (uint) {
61. require(msg.sender.call());
62. return 7;
63. }
64.}
如果同一个函数有多个修饰器modifier,它们之间以空格隔开,修饰器modifier会依次检查执行。
注:在早期的Solidity版本中,有修饰器modifier的函数,return语句的行为表现不同。
修饰器modifier或函数体中显式的return语句仅仅跳出当前的修饰器modifier和函数体。返回变量会被赋值,但整个执行逻辑会从前一个修饰器modifier中的定义的“_”之后继续执行。
修饰器modifier的参数可以是任意表达式,在此上下文中,所有在函数中可见的符号,在修饰器modifier中均可见。在修饰器modifier中引入的符号在函数中不可见(可能被重载改变)。
1.2.4代码注释
可以使用单行注释(//)和多行注释(/…/)
1.// 这是一个单行注释。
2.
3./*
4.这是一个
5.多行注释。
6.*/
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)