智能合約語法筆記

區塊鏈語法筆記

demo 鑄幣代碼

pragma solidity ^0.4;
contract Coin{
    //set the "address" type variable minter
    address public minter; 
    /*convert "address"(for storing address or key ) 
    to the type of "uint" which is as subscrip of object balances*/
    mapping (address =>uint) public balances; 
    // set an event so as to be seen publicly
    event Sent(address from,address to,uint amount); 
    //constructor only run once when creating contract,unable to invoke
    //"msg" is the address of creator."msg.sender"  is 
    constructor()public{
        minter=msg.sender;
    }
    //鑄幣
    //can only be called by creator
    function mint(address receiver,uint amount)public{
        require(msg.sender ==minter);
        balances[receiver]+=amount;
    }
    //轉賬 
    function send(address receiver,uint amount)public{
        require(balances[msg.sender]>= amount);
        balances[msg.sender]-=amount;
        balances[receiver]+=amount;
        emit Sent(msg.sender,receiver,amount);
    }

}

源文件結構

pragma 版本標識

只對本文件有效,如果導入其他文件,版本標識不會被導入,啟動編譯器檢查

^0.5.2;  #從0.5.2到0.6(不含)的版本
import 導入文件
import * as symbolName from "filename";  #等價
import "filename" as symbolName;

狀態變量

狀態變量是永久地存儲在合約存儲中的,有基本類型.函數外的都是store狀態變量。

  • bool,
  • int/uint(有符號和無符號)
  • fixed / ufixed (有符號和無符號的定長浮點型。)
在關鍵字 ufixedMxN 和 fixedMxN 中,M 表示該類型占用的位數,N 表示可用的小數位數。 M 必須能整除 8,即 8 到 256 位。 N 則可以是從 0 到 80 之間的任意數。 ufixed 和 fixed 分別是 ufixed128x19 和 fixed128x19 的別名
  • address 地址類型
address:保存一個20字節的值(以太坊地址的大小)。
address payable :可支付地址,與 address 相同,不過有成員函數 transfer 和 send
address payable 可以完成到 address 的隱式轉換,但是從 address 到 address payable 必須顯示的轉換, 通過 payable(<address>) 進行轉換
  • bytes1 定長字節數組

  • bytes和string,uint[] 變長字節數組

  • 多維數組的下標和一般是相反的,a[2][4]表示4個子數列,每個子數列里2個元素

函數

function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]

函數是代碼的可執行單元。函數通常在合約內部定義,但也可以在合約外定義。

函數可以作為參數傳入

可見性

https://solidity-by-example.org/visibility/

內部(internal) 函數類型,只能在當前合約內被調用

外部(external) 函數類型,由一個地址和函數簽名組成,在調用時會被視作function類型

external: 外部函數作為合約接口的一部分,可以被交易或者其他合約調用。 外部函數 f 不能以內部調用的方式調用(即 f 不起作用,但 this.f() 可以)。

public: public 函數是合約接口的一部分,可以在內部或通過消息調用。對于 public 狀態變量, 會自動生成一個 getter 函數。

internal : 只能在當前合約內部或它子合約中訪問,不使用 this 調用。

private: private 函數和狀態變量僅在當前定義它們的合約中使用,并且不能被派生合約使用(如繼承)

有且僅有以下三種轉化:

  • pure 函數可以轉換為 viewnon-payable 函數
  • view 函數可以轉換為 non-payable 函數
  • payable 函數可以轉換為 non-payable 函數
參數和返回值
修飾符

https://solidity-by-example.org/function-modifier/

函數修飾符用來修飾函數,比如添加函數執行前必須的先決條件.函數修飾器通過繼承在派生合約中起作用。

 modifier onlyOwner { 函數體會插入在修飾函數的下劃線_的位置
      require(msg.sender == owner);
      _;
   }
如果一個函數中有許多修飾器,寫法上以空格隔開,執行時依次執行:首先進入第一個函數修飾器,然后一直執行到_;接著跳轉回函數體,進入第二個修飾器,以此類推。到達最后一層時,一次返回到上一層修飾器的_;后。
自由函數

定義在合約外的函數叫做自由函數,一定是internal類型,就像一個內部函數庫一樣,會包含在所有調用他們的合約內,就像寫在對應位置一樣。但是自由函數不能直接訪問全局變量和其他不在作用域下的函數(比如,需要通過地址引入合約,再使用合約內的函數)

view

view函數不能產生任何修改。由于操作碼的原因,view庫函數不會在運行時阻止狀態改變,不過編譯時靜態檢查器會發現這個問題。

以下行為都視為修改狀態:

  1. 修改狀態變量。
  2. 觸發事件。
  3. 創建其它合約。
  4. 使用 selfdestruct
  5. 通過調用發送以太幣。
  6. 調用任何沒有標記為 view 或者 pure 的函數。
  7. 使用低級調用。
  8. 使用包含特定操作碼的內聯匯編。
receive 函數

一個合約至多有一個receive函數,形如receive() external payable { ... } ,注意沒有function 的標識,沒有參數,只能是externalpayable標識,可以有函數修飾器,支持重載。

receive函數在沒有任何調用數據時執行(如用.send()或者.transfer()給合約轉賬),如果沒有設置receive函數,那么就會執行fallback函數,如果這兩個函數都不存在,合約就不能通過交易的形式獲取以太幣

回退函數

一個合約至多一個回退函數,格式如:fallback () external [payable] 或者 fallback (bytes calldata _input) external [payable] returns (bytes memory _output),后者的函數參數會接收完整的調用信息(msg.data),返回的時未經修改的數據(如為經過ABI編碼)。

回退函數可以時virtual的,可以重載,也可以被修飾器修飾。在函數調用時,如果沒有與之匹配的函數簽名或者消息調用為空且無receive函數,就會調用fallbakc函數。

如果回退函數代替了receive函數,那么仍然只有2300gas可用。

構造函數
contract X {
    string public name;
    constructor(string memory _name) {
        name = _name;
    }
}
// Base contract Y
contract Y {
    string public text;
    constructor(string memory _text) {
        text = _text;
    }
}
兩種方式
contract B is X("Input to X"), Y("Input to Y") {
}
// Order of constructors called:
// 1. X
// 2. Y
// 3. C
contract C is X, Y {
    // Pass the parameters here in the constructor,
    // similar to function modifiers.
    constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}

事件

事件是能方便地調用以太坊虛擬機日志功能的接口,分為設置事件和觸發事件

pragma solidity >=0.4.21 <0.9.0;
contract TinyAuction {
    event HighestBidIncreased(address bidder, uint amount); // 事件

    function bid() public payable {
        // ...
        emit HighestBidIncreased(msg.sender, msg.value); // 觸發事件
    }
}

事件是對EVM日志的簡短總結,可以通過RPC接口監聽。觸發事件時,設置好的參數就會記錄在區塊鏈的交易日志中,永久的保存,但是合約本身是不可以訪問這些日志的。可以通過帶有日志的Merkle證明的合約,來檢查日志是否存在于區塊鏈上。由于合約中僅能訪問最近的 256 個區塊哈希,所以還需要提供區塊頭信息。

繼承

https://solidity-by-example.org/inheritance/

當合約繼承其他的合約時,只會在區塊鏈上生成一個合約,所有相關的合約都會編譯進這個合約,調用機制和寫在一個合約上一致。

所以如果繼承了多個合約,希望把所有的同名函數都執行一遍,就需要super關鍵詞。

函數重寫

父合約中被標記為virtual的非private函數可以在子合約中用override重寫。

重寫可以改變函數的標識符,規則如下:

  • 可見性只能單向從 external 更改為 public。
  • nonpayable 可以被 viewpure 覆蓋。
  • view 可以被 pure 覆蓋。
  • payable 不可被覆蓋。
  • 函數修飾器也支持重寫,且和函數重寫規則一致
// Contracts inherit other contracts by using the keyword 'is'.
contract B is A {
    // Override A.foo()
    function foo() public pure virtual override returns (string memory) {
        return "B";
    }
}

contract C is A {
    // Override A.foo()
    function foo() public pure virtual override returns (string memory) {
        return "C";
    }
}
contract D is B, C {
    // D.foo() returns "C"  執行最遠的那個父類函數C的調用
    // since C is the right most parent contract with function foo()
    function foo() public pure override(B, C) returns (string memory) {
        return super.foo();
    }
}

接口

接口和抽象合約的作用很類似,但是它的每一個函數都沒有實現,而且不可以作為其他合約的子合約,只能作為父合約被繼承。

接口中所有的函數必須是external,且不包含構造函數和全局變量。接口的所有函數都會隱式標記為external,可以重寫。但是多次重寫的規則和多繼承的規則和一般函數重寫規則一致。

  • 不能實現任何功能
  • 可以從其他接口繼承
  • 所有聲明的函數必須是外部的
  • 不能聲明構造函數
  • 不能聲明狀態變量

interface ICounter {
    function count() external view returns (uint);
    function increment() external;
}

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

引用類型

引用類型可以通過不同變量名來修改指向的同一個值。目前的引用類型包括:結構體、數組和映射

  • memory:存儲在內存里,只在函數內部使用,函數內不做特殊說明為memory類型。
  • storage:相當于全局變量。函數外合約內的都是storage類型。
  • calldata:保存有函數的參數,不可修改,大多數時候和memory相似。它常作為外部函數的參數,也可以當作其他的變量使用
  • 盡可能使用calldata,因為它既不會復制,也不能修改,而且還可以作為函數的返回值
  • storagememory之間的賦值或者用calldata對它們賦值,都是產生獨立的拷貝,不修改原來的值,其他向storage 賦值是拷貝,結構體里面的賦值是一個拷貝。
  • 于數組和結構體在函數中要有存儲位置聲明

基本類型轉換

隱式轉換:隱式轉換發生在編譯時期,如果不出現信息丟失,其實都可以進行隱式轉換,比如uint8可以轉成uint16。隱式轉換常發生在不同的操作數一起用操作符操作時發生。

顯式轉換:如果編譯器不允許隱式轉換,而你足夠自信沒問題,那么就去嘗試顯示轉換,但是這很容易造成安全問題

如果是uint或者int同類型強制轉換,就是從最低位截斷

單位、內置函數和變量

  • 幣的單位默認是wei,也可以添加后綴。
1 wei == 1;
1 gwei == 1e9;
1 ether == 1e18;

區塊和交易屬性

  • blockhash(uint blockNumber) returns (bytes32):指定區塊的區塊哈希,但是僅可用于最新的 256 個區塊且不包括當前區塊,否則返回0.

  • block.chainid (uint): 當前鏈 id

  • block.coinbase ( address ): 挖出當前區塊的礦工地址

  • block.difficulty ( uint ): 當前區塊難度

  • block.gaslimit ( uint ): 當前區塊 gas 限額

  • block.number ( uint ): 當前區塊號

  • block.timestamp ( uint): 自 unix epoch 起始當前區塊以秒計的時間戳

  • gasleft() returns (uint256) :剩余的 gas

  • msg.data ( bytes ): 完整的 calldata

  • msg.sender ( address ): 消息發送者(當前調用)

  • msg.sig ( bytes4 ): calldata 的前 4 字節(也就是函數標識符)

  • msg.value ( uint ): 隨消息發送的 wei 的數量

  • tx.gasprice (uint): 交易的 gas 價格

  • tx.origin (address payable): 交易發起者(完全的調用鏈)

    錯誤處理

    assert(bool condition),require(bool condition),require(bool condition, string memory message)均是條件為假然后回滾。

    revert(),revert(string memory reason) 立即回滾

內部調用

內部調用再EVM中只是簡單的跳轉,傳遞當前的內存的引用,效率很高。但是仍然要避免過多的遞歸,因為每次進入內部函數都會占用一個堆棧槽,而最多只有1024個堆棧槽。

外部調用

  • 只有external或者public的函數才可以通過消息調用而不是單純的跳轉調用,外部函數的參數會暫時復制在內存中。
  • 注意this不可以出現在構造函數里,因為此時合約還沒有完成。
  • 調用時可以指定 value 和 gas 。這里導入合約使用的時初始化合約實例然后賦予地址。

元組的賦值行為

函數的返回值可以是元組,因此就可以用元組的形式接收,但是必須按照順序排列。在0.5.0之后,兩個元組的大小必須相同,用逗號表示間隔,可以空著省略元素。注意,不允許賦值和聲明都出現在元組里,比如(x, uint y) = (1, 2);不合法。

元祖就是多個不同類型數據組成的數組

錯誤

assert函數,用于檢查內部錯誤,返回Panic(uint256),錯誤代碼分別表示:

  1. 0x00: 由編譯器本身導致的Panic.
  2. 0x01: assert 的參數(表達式)結果為 false 。
  3. 0x11: 在unchecked { … }外,算術運算結果向上或向下溢出。
  4. 0x12: 除以0或者模0.
  5. 0x21: 不合適的枚舉類型轉換。
  6. 0x22: 訪問一個沒有正確編碼的storagebyte數組.
  7. 0x31: 對空數組 .pop()
  8. 0x32: 數組的索引越界或為負數。
  9. 0x41: 分配了太多的內存或創建的數組過大。
  10. 0x51: 如果你調用了零初始化內部函數類型變量

Error(string)的異常由編譯器產生,有以下情況:

  1. require 的參數為 false
  2. 觸發revert或者revert("discription")
  3. 執行外部函數調用合約沒有代碼。
  4. payable 修飾的函數(包括構造函數和 fallback 函數),接收以太幣。
  5. 合約通過 getter 函數接收以太幣 。

try/catch
try后面只能接外部函數調用或者是創建合約new ContractName的表達式,并且表達式內部的錯誤并不會被記錄,只有調用的外部函數內出現錯誤才會返回回滾的信息。如果有returns的話,后面接外部函數的返回值類型,return后面是當前合約函數的返回值。

地址

以太坊地址是給定公鑰哈希的最后 20 個字節。使用的散列算法是Keccak-256。所以對于一個唯一的私鑰 => 唯一的哈希.

所有錢包都應該接受以大寫或小寫字符表示的以太坊地址

  • 您可以將 Ether 發送到定義為的變量address payable

  • 您不能將 Ether 發送到定義為的變量address

  • msg.sender() -> Returns : address payable

  • tx.origin() -> Returns : address payable

  • .transfer()` , `.send()` , `.call()` ,
    `.delegatecall()` and `.staticcall() 
    是address payable 定義變量的用法
    
  • address 具有balance方法,表示eth余額

  • address payable` to `address 隱式轉換可以,返過來不可以
    
    contract NotPayable {
    }
    contract Payable {
         function() payable {}
    }
    contract HelloWorld {
         address x = address(NotPayable); //address類型
         address y = address(Payable); //address payable類型,因為該合同有payable類型的回調函數
         function hello_world() public pure returns (string memory) {
              return "hello world";
         }
    }
    

EVM 提供了4種 特殊的操作碼來與其他智能合約交互,其中 3 種可用作以下address類型的方法:**call****delegatecode****staticcall**

call:

https://solidity-by-example.org/call/

address.call(bytes memory) returns (bool, bytes memory)

我是合約 A,我想為合約 B 存儲執行合約 B 功能。調用 B.function() 只能更新 B 的存儲

callcode
address.callcode(__payload__)

*.callcode()*現在已棄用,取而代之的是*.delegatecall()*. 但是,仍然可以在內聯匯編中使用它。

合約A本質上是復制B的功能

我是合約 A,我想為我的存儲執行合約 B 的功能。調用 B.function() 將更新 A 的存儲

delegatecall

https://solidity-by-example.org/delegatecall/

_address.**delegatecall(**bytes memory**)** returns (bool, bytes memory)

我是合約A,我想執行合約B的功能,但是合約B可以偽裝成我。

B 的函數可以覆蓋 A 的存儲,并在任何其他合約中偽裝成A。

msg.sender 將是 A 的地址,而不是 B

在這種情況下,合約 A 本質上是將函數調用委托給 B。與前callcode一種方法的不同之處在于,使用delegatecallnot only enable 覆蓋合約 A 的存儲。

如果合約 B 調用另一個合約 C,合約 C 將看到它msg.sender是合約 A

把B的代碼在A的執行環境中執行,數據使用A的

staticcall
address.**staticcall(**bytes memory**)** returns (bool, bytes memory)

規格

  • 低級STATICCALL,請參閱操作碼 OxF4)(具有當前合約看到的完整 msg 上下文)給定作為參數傳遞的memory(數據有效負載)。
  • 返回一個元組:

當交易在接收者字段中指定一個稱為零地址的特定地址時,它打算創建一個新合約.向該地址發送資金實際上不會轉移任何以太幣。在以太坊網絡中,礦工將包含此接收者的交易解釋為創建新智能合約的指令。

應付

用來接收和提取eth

contract Payable {
    // Payable address can receive Ether
    address payable public owner;

    // Payable constructor can receive Ether
    constructor() payable {
        owner = payable(msg.sender);
    }
        //把合約里的amount提取到owener地址上啊,call在那個地址上調用,就更新那個地址的數據
    // Function to withdraw all Ether from this contract.
    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;

        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // Function to transfer Ether from this contract to address from input
    function transfer(address payable _to, uint _amount) public {
        // Note that "to" is declared as payable
        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Failed to send Ether");
    }
}
contract ReceiveEther { 接受者接收的邏輯
    /*
    Which function is called, fallback() or receive()?

           send Ether
               |
         msg.data is empty?
              / \
            yes  no
            /     \
receive() exists?  fallback()
         /   \
        yes   no
        /      \
    receive()   fallback()
    */

    // Function to receive Ether. msg.data must be empty
    receive() external payable {}

    // Fallback function is called when msg.data is not empty
    fallback() external payable {}

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

contract SendEther {
消息調用者把比發給to這個地址
    function sendViaTransfer(address payable _to) public payable {
        // This function is no longer recommended for sending Ether.
        _to.transfer(msg.value);
    }
消息調用者把比發給to這個地址,需要發送成功,
    function sendViaSend(address payable _to) public payable {
        // Send returns a boolean value indicating success or failure.
        // This function is not recommended for sending Ether.
        bool sent = _to.send(msg.value);
        require(sent, "Failed to send Ether");
    }
消息調用者把比發給to這個地址,需要發送成功,
    function sendViaCall(address payable _to) public payable {
        // Call returns a boolean value indicating success or failure.
        // This is the current recommended method to use.
        (bool sent, bytes memory data) = _to.call{value: msg.value}("");
        require(sent, "Failed to send Ether");
    }
}

回調函數fallback

fallback是一個不接受任何參數且不返回任何內容的函數。

它在何時執行

  • 調用不存在的函數或
  • 以太幣直接發送到合約但receive()不存在或msg.data不為空
  • fallback被transfer or send.調用時有 2300 氣體限制

功能選擇器

調用函數時,前 4 個字節calldata指定調用哪個函數。

下面的這段代碼。它用于在地址上call執行transfer合約addr。
addr.call(abi.encodeWithSignature("transfer(address,uint256)", 0xSomeAddress, 123))

調用其他合約

https://solidity-by-example.org/calling-contract/

最簡單的方法就是直接調用它,比如A.foo(x, y, z).

調用其他合約的另一種方法是使用低級call.

try/Catch

contract Foo {
    address public owner;

    constructor(address _owner) {
        require(_owner != address(0), "invalid address");
        assert(_owner != 0x0000000000000000000000000000000000000001);
        owner = _owner;
    }

    function myFunc(uint x) public pure returns (string memory) {
        require(x != 0, "require failed");
        return "my func was called";
    }
}

contract Bar {
    event Log(string message);
    event LogBytes(bytes data);
     function tryCatchNewContract(address _owner) public {
        try new Foo(_owner) returns (Foo foo) {
            // you can use variable foo here
            emit Log("Foo created");
        } catch Error(string memory reason) {
            // catch failing revert() and require()
            emit Log(reason);
        } catch (bytes memory reason) {
            // catch failing assert()
            emit LogBytes(reason);
        }
    }
}    

ERC20

任何遵循ERC20 標準的合約都是 ERC20 代幣。該標準提供了代幣的基本功能:如轉移代幣,授權代幣給其他人(如鏈上第三方應用)使用。標準接口允許以太坊上的任何代幣被其他應用程序重用,如錢包、去中心化交易所等

這是一個發幣的合約,平日里所接觸的許許多多代幣如usdt(erc20)、usdc、dai、unsiwap、chainlink、wbtc、sushi等等絕大都數都是erc20代幣,

ERC20 代幣提供以下功能

  • 轉移代幣
  • 允許其他人代表代幣持有者轉移代幣
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IERC20 {
    function totalSupply() external view returns (uint);

    function balanceOf(address account) external view returns (uint);

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

    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint amount) external returns (bool);

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

    event Transfer(address indexed from, address indexed to, uint value);
    event Approval(address indexed owner, address indexed spender, uint value);
}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373

推薦閱讀更多精彩內容