びっとこいんのほわいとぺーぱー
はじめに
この記事はビットコインのホワイトペーパーを読んで要点を分かりやすく解説したものです。
他にもいろんなチェーンのホワイトペーパーを出すつもりなので、気に入ったらそちらも見てくれると嬉しいです!
ホワイトペーパー
目的
ビットコインの最大の目的は二重支払い問題の解決です。二重支払い問題とは同じコインが何度も使用されてしまう問題です。
実際の世界ではお金は硬貨や紙幣として存在しているため、同じお金を別の支払いに使うことは物理的にできません。コンビニでお菓子を買ったときのお金は自分の手元からなくなりますよね?
しかし、お金をデジタル化したときには簡単にコピーすることができてしまいます。ビットコインはこれを防止することを目的として作られました。
ここでお気づきの方もいるかも知れませんが、Visaや交通系ICカードなどはブロックチェーンなどを使わずともすでに現金のデジタル化に成功しています。これは、サービスを提供するサーバーや管理者を信頼することを前提で成り立っています。サーバーは必ず正しい処理をするか、サーバーのセキュリティは問題ないか、サービスの管理者は絶対に不正をしないか、そういった不確定要素を見ないふりをして私達は電子マネーを使っています。
ビットコインは誰かを信頼することなく動くシステムなのでこの問題も解決することができます。
事前知識
まずは、ビットコインを理解するのに重要な2つの技術を紹介します。
ハッシュ関数
ハッシュ関数とは、文字を入力したときに一定の長さのランダムな16進数を返すような関数です。重要なのは、同じ文字を入力したときには同じ文字を返すという性質です。
1. ビットコイン => b89ae3280372363341c1acef577b58d1e5b7df65a3775c7c8f1788aef9a04b662
2. イーサリアム => 634f66b919cb35371d7b5b0618816b5e606e395c06efa956fa1ff9065db4fa6f
3. ビッタコイン => 1f836071593982ef040a788f399059cded4d56ca5d7037fb3229e4b5684d8f65
出力される文字は、入力が一文字変わるだけで全く違うものになります。
Private Key / Public Key
Private KeyとPublic Keyは安全にデータを送信するために作られた暗号技術です。2つの鍵をセットで使います。
Public Keyを使って暗号化されたデータは、Private Keyを使うことでしか復号できません。また、Public KeyからPrivate Keyを予測することもできません。
詳しくはこちら↓
1. Transaction
誰から誰にいくらビットコインを送ったのかという情報を保存するための仕組みをTransactionといいます。
新しいTransactionを発行する場合は送り先の人のPublic Keyを調べてそれをTransactionに入れる必要があります。さらに自分のPrivate Keyで暗号化した、電子署名と言われるデータを含めることでTransactionの送り主が正しいことを証明できます。

2. タイムスタンプサーバー
タイムスタンプサーバーとは、直前のハッシュ値とデータをハッシュ化して次のデータに含めることでどこか一文字でもデータが改ざんされたときに検知できるシステムです。このデータはこのタイミングで存在していたという証明になるという役割も果たします。

3. Proof of Work (PoW)
Proof of Workは、ブロックチェーンの核となる技術です。
下の図を見てもらうと分かりますが、先程のタイムスタンプサーバーにめちゃくちゃ似てますね。違いはItemがTx(Transaction)になっていることと、Nonceという要素がブロックに含まれていることです。このNonceが重要な役割を果たします。

ブロックチェーン上に新しいブロックを作ってその中にTxを保管しようと思ったとき、Nonce(Number used once)という数字を見つけ出さないといけません。Nonceというのはブロックをハッシュ化したときに、そのハッシュ値の先頭のいくつかに0が並ぶような値です。
例) 0x0000af7238bd0c
この例では先頭に4つの0が並んでいます。ビットコインネットワークでは、いくつ0が並ぶ必要があるのかはネットワークに参加しているノード(参加しているコンピュータのこと)の数と全CPUパワーの合計によって決まります。10分に一回発見されるような難易度に自動的に調整されるのです。
ハッシュ化した後の値は予測することができないので、ひたすら色々な数を入れて計算してみなければいけません。そのため、CPUパワーの高いコンピューターほど高い確率でNonceを発見できます。
もしも、複数のノードが同時に異なるNonceを見つけた場合はどうなるのでしょうか?このときにはチェーンは分岐します。

そして十分に時間がたった後に一番長いチェーンが正しいチェーンとして採用され、採用されなかった方のチェーンは破棄されます。この方法によって悪意のあるTxを含むブロックは採用されなくなります。詳しくは次の章で解説。
4. ネットワーク
ビットコインネットワーク上で新しいTransactionが発行されたときの流れは以下のようになります。
1. 新しい取引は全ノードに送信。
2. 各ノードが新しい取引をブロックに取り入れる。
3. 各ノードがそのブロックへのPoWを算出。
4. PoWを見つけ次第全ノードへ送信。
5. ノードはブロックに含まれるすべてのTransactionが有効で、以前に使われていない場合のみ承認。
すべてのTransactionにはTransaction IDというものが付与されており、過去の取引をそのIDで検索することで以前に使われていないか検証できます。
正しくないブロックは良心的なノードによって破棄されるため、結果的に正しいチェーンのみが残ります。
5. インセンティブ
ブロックの中の最初のTxは新しいコインを発行するための特別なものとなります。そのコインはブロックの作成者(Nonceを見つけた人)のものとなるため、ビットコインマイナーと呼ばれる人たちはブロック生成時の新規コイン発行によって報酬を得ます。
また、Txが発行されるたびに手数料を受け取ることもでき、この2つの報酬によってマイナーがマイニングをする動機づけができます。
6. ディスクスペースを節約する
全ての取引はノードとなるコンピュータのメモリー内に保存されるが、Txが発行されるたびにデータは増え続ける一方であるためメモリー量を減らさなければならない。これは、マークル木と呼ばれる技術によって実現できる。

マークル木では、2つのTxのハッシュ値をまとめてハッシュ化して一つのハッシュ値を作り、更に他のまとめられたハッシュ値とまとめてハッシュ化し、、、と繰り返す事によって最終的に複数のTxを一つのハッシュ値(Root Hash)にまとめます。Txのどれかに改ざんがあるとRoot Hashの値が変わるため、改ざんを検知できます。
ここでもしも上の図のTx0とTx1のデータを参照する必要がなくなった場合、Hash01の値だけを残しておけば改ざんは検知できます。使わなくなったデータを捨ててハッシュ値だけ残しておくことでメモリーを節約することができます。
7. SPV(Simplified Payment Verification)
SPVとは先程の図のブロックヘッダーのみを持っているノードのことを指します。ブロックヘッダーのみを持つことで改ざんの検知ができ、更に必要なメモリーは1000分の1程度まで減ります。
しかし、過去のTxに関する情報が失われてしまうため二重支払いに関する検証を行えません。そこで、それらの検証については全てのTxを保存しているフルノードと呼ばれるノードに依頼します。
-------- よもやま話 -------------------------------
フルノードであることによるインセンティブはありませんが、暗号資産の取引所などは強固なセキュリティと素早い二重支払いなどの不正の検知が必要なためフルノードを建てる必要があります。
ビットコインネットワークは善良なフルノードによって支えられており、データ量が増えるほどフルノードの数は減っていくため、中央集権化が進むことが問題視されています。
---------------------------------------------------
8. 価値の分割
誰かに自分の保有しているコインを送るとき、自分宛に送られた未使用のTxを使用することとなる。
もし自分宛に10コインを送るというTxと15コインを送るというTxの2つのTxある場合自分の保有しているコインは25枚となります。
20コインを送りたいと考えたとき、Txは分割できないため10コインと15コインでは20コインピッタリ送ることはできません。そこで2つのTxを集めて20コインを相手に贈り、5コインを自分に送るという2つのTxを作成することで実質的に20コインを送ることができます。自分宛ての5コインはお釣りと考えても良いかもしれません。
9. プライバシー
プライバシーを保つための方法の一つとして、Public Keyを匿名にすることが挙げられます。
通常Publicキーは誰が発行しているのか公開されるが、匿名で公開することで誰かが誰かにコインを送ったことはわかるが、それが誰なのかは明かされません。
さらに一回の取引ごとに新しいPublic KeyとPrivate Keyのペアを作ることで誰がどのTxを発行しているのか分からなくなります。しかし、Txを発行するときに複数のTxをまとめる場合、それらのPublic Keyは同一人物が発行したものであることが分かってしまうため一定のリスクは避けられません。
10. 改ざんリスク
ここからは少し数学的な話となるため、苦手な人は読み飛ばしてもらって構わないです。
攻撃者がチェーンを乗っ取ることのできる確率について考えていきます。
攻撃者がチェーンを乗っ取るためには良心的なノードが作るブロックの数に攻撃者が作るブロックの数が追いつかなければなりません。
良心的なノードが次のブロックを見つける確率をp、攻撃者が次のブロックを見つける確率をqとして攻撃者がzブロック先のブロックに追いつく確率qzを考えると、
[math]q\tiny z\normalsize =\begin{cases}1 & p \leq q\\\left(\frac{p}{q}\right)^{z} & p > q\end{cases}[/math]
と表される。qzは指数的に小さくなっていくため、ブロックが多く連なると事実上攻撃不可能なところまで確率は下がっていく。
さらに、Txを発行して何ブロック待てば安全と言えるのかを考えてみたい。
自分のTxが発行されてからw個のブロックが追加されるのを待つ。このとき、攻撃者の正確な進捗はわからないが攻撃者の進捗がkである確率P(k)は以下のポアソン分布で求められる。
[math]\lambda=w\frac{p}{k}[/math]
[math]P(k)=\frac{e^{-\lambda}\times\lambda^{k}}{k!}[/math]
攻撃者の追いつく確率Pを求めるには、攻撃者の進捗がkである確率にkの遅れから追いつく確率をかけ合わせれば良いので
[math]P = \sum_{k=0}^{\inf}\frac{e^{-\lambda}\lambda^k}{k!}\times\begin{cases}(\frac{q}{p})^{(z-k)} & k \leq z\\1 & k > z\end{cases}=1-\sum_{k=0}^{z}\frac{e^{-\lambda}\lambda^k}{k!}\left\{1-(\frac{q}{p})^{(z-k)}\right\}[/math]
これにより攻撃者の追いつく確率を求めることができる。
まとめ
ビットコインの論文はなんと9ページで終わっています。9ページで世界を変える発明、すごいですね。でも実際はすでにある技術の組み合わせなので、こういうのを発明と言うんだろうなって思います。さとしすげー。
ChainLinkを使ってIPFSからデータを取ってきた
ChainLinkを使ってIPFSからデータを引っ張ってきたときの備忘録。
Pinataを使ってデータをIPFSに登録
Pinataを使ってこのデータをIPFS上に保存。
{ "HP": 100 }
実際にデータを撮ってみる
Remixを使って開発していきます。まずは、ライブラリのインポート。
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
つぎに、これらのコードを継承。
contract myContract is ChainlinkClient, ConfirmedOwner { using Chainlink for Chainlink.Request; constructor() ConfirmedOwner(msg.sender) { setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB); setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7); } }
setChainlinkTokenのところには$LINKトークンのアドレスを、setChainlinkOracleにはどのチェーンを使用するか選択します(今回はgoerliネット)。
詳しくは以下を参照。
Testnet Oracles | Chainlink Documentation
次に、実際にデータを取得するコードを書いてみる。
function requestHP() public returns (bytes32 requestId) { Chainlink.Request memory req = buildChainlinkRequest("ca98366cc7314957b8c012c72f05aeeb", address(this), this.fullfill.selector); req.add("get", "https://gateway.pinata.cloud/ipfs/QmdctF9rpwsTRnZHScFhu5m2ss1VoFKdtfsgVskpLbNwDp"); req.add("path", "HP"); req.addInt("times", 1); return sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10); } function fullfill(bytes32 _requestId, uint256 _HP) public recordChainlinkFulfillment(_requestId) { HP = _HP; } function getHP() public view returns (uint256) { return HP; }
これで、requestHP関数を呼んだあとにgetHP関数を呼び出してデータを取得することができるようになりました。
一個ずつ解説。
- buildChainlinkRequest
第一引数に「jobId」を指定する。jobIdというのはChainlinkでどのようなデータ型を扱いたいかを指定するためのID。今回はuint256型を取得するためのIDを指定した。
他の型についても、先程のリンクに書いてある。
Testnet Oracles | Chainlink Documentation
第二引数にはこのコントラクトのアドレスを、第三引数にはコールバック関数(値が取得できた場合に呼び出される関数)を指定する。
- req.add("get", ...)
getリクエストを実行するということを宣言。
- req.add("path", "HP")
取得したJsonデータのうちのどこにあるデータを使用するか指定。
例えば、以下のようなJsonが返ってきた場合を例に考えると
[ { "greeting": "good mornin" }, { "greeting": "good night" } ]
この場合は
req.add("path", "0, greeting");
とすることで取得できる。
今回はHPが取得したいのでこのような形となる。
- req.addInt("times", 1)
これは、数値を受け取る場合のみに必要なプロパティである。受け取った数字を何倍かして返したい場合に指定する(何もかけない場合は1を指定)。
10 ** 18 などがよく指定される。
- sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10)
第二引数には支払う$LINKの量が指定される。今回は0.1Link支払っている。
実行
実際にデータを取得してみる。
デプロイボタンを押して、requestHP関数を呼び出せばよいだけだが一つ注意点として、$LINKをデプロイしたコントラクト宛に送ってやらないと関数の呼び出しが失敗してしまう。
(僕はこれに気がつくのに1時間かかりました、、、)
requestHPを呼び出した後にgetHPを呼び、100という数字が返ってきたら成功!!おつかれ!!!
全体のコード
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol"; contract myContract is ChainlinkClient, ConfirmedOwner { using Chainlink for Chainlink.Request; constructor() ConfirmedOwner(msg.sender) { setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB); setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7); } function requestHP() public returns (bytes32 requestId) { Chainlink.Request memory req = buildChainlinkRequest("ca98366cc7314957b8c012c72f05aeeb", address(this), this.fullfill.selector); req.add("get", "https://gateway.pinata.cloud/ipfs/QmdctF9rpwsTRnZHScFhu5m2ss1VoFKdtfsgVskpLbNwDp"); req.add("path", "HP"); req.addInt("times", 1); return sendChainlinkRequest(req, (1 * LINK_DIVISIBILITY) / 10); } function fullfill(bytes32 _requestId, uint256 _HP) public recordChainlinkFulfillment(_requestId) { HP = _HP; } function getHP() public view returns (uint256) { return HP; }
ERC721のNFTをERC20のトークンでmintする
ERC721で作ったトークンをERC20のトークンを支払うことでmintする仕組みを作ろうと思ったときに、意外と手間取ったのでそのやり方を書いていきます。
この記事は、Remix IDEを使ったことがある人が対象です。
ERC20, ERC721のトークンを作る
簡単に、OpenZeppelinのwizard機能を使って作っていきます。
docs.openzeppelin.com
↓コード
ERC20.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MyToken is ERC20 { constructor() ERC20("MyToken", "MTK") { _mint(msg.sender, 10000 * 10 ** decimals()); } }
ERC721.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyToken is ERC721, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() ERC721("MyToken", "MTK") {} function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } }
ERC721.solの中でERC20.solのコードを実行できるようにする。
OpenZeppelinのIERC20というライブラリをインポートすると、トークンのアドレスからそのトークンの関数を実行できるようになる。
ERC721.sol
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // ライブラリのインポート import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyToken is ERC721, Ownable { using Counters for Counters.Counter; IERC20 public tokenAddress; uint256 rate = 100 * 10 ** 18; // NFTのmintに100 Token必要 Counters.Counter private _tokenIdCounter; constructor(address _tokenAddress) ERC721("My Token", "MTK") { tokenAddress = IERC20(_tokenAddress); // 最初に参照先のアドレスを指定。 } ...
safeMint内でERC20の関数を実行
safeMintの中でERC20のtransferFrom関数を実行する。
ERC721.sol
...
function safeMint() public {
tokenAddress.transferFrom(msg.sender, address(this), rate);
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(msg.sender, tokenId);
}
...
コントラクトのdeploy & 実行
まずは、ERC20.solをデプロイ。普通にデプロイボタンを押すだけで大丈夫。
デプロイが完了したらコントラクトアドレスをコピーしておき、ERC721のデプロイ時に使用する。
ERC721のデプロイが完了したら、早速使ってみる。
普通にsafeMintを呼び出すとエラーが起きるので、まずはERC20.solの「approve」関数を実行しておく。mintに必要なトークン量は100 Tokenなので、「100 * 10 ** 18」をapproveする。spender引数にはERC721のコントラクトを入れる。approveが成功したら、safeMintが実行できるので実行する。
safeMintが完了したら、本当にmintされているか確認する。「balanceOf」関数にsafeMintを実行したときのアカウントのアドレスを入れる。1という値が帰ってきたら成功だ。
終わり
今回の方法はコントラクトアドレスさえわかれば既存のERC20 Tokenにも応用できるので一気にコードの幅が広がります!皆もぜひやってみてね!
Shh Bzzってなんだろう?(web3.js)
web3.jsのドキュメントを読んでいるとShhとBzzという単語に遭遇した。
ドキュメントを読んでもよく分からなかったので、リサーチしたレポ。
web3.jsとは(web3.jsの基本概念や特徴)|Ethereum JavaScript Libraries: web3.js vs. ethers.js (Part I)[邦訳]
この記事がわかりやすかった。
Shh
web3-shhモジュールを使うとWhisperプロトコルと対話することが出来る。
Whisperプロトコルというのはメッセージをブロードキャストしたり、低レベルの非同期通信をするために設計されたプロトコル。
以下の2つのメソッドがよく使われる。
web3.shh.post // ネットワークにメッセージを投稿 web3.shh.subscribe // メッセージを受信するサブスクライブを作成
Bzz
web3-bzzを使うとSwarmと対話することが出来る。Swarmは分散画家のストレージプラットフォームのこと。Dapps用の画像や動画などのファイルを保存する場所として使われる。
以上!
Crypto Zombieで始めるSolidity入門⑥
この記事は前回の続きです。
前回の記事はこちら↓
chanichiwasshoi.hateblo.jp
このシリーズではCrypto ZombieというSolidityの学習が出来るサイト(しかも無料で)の内容を自分なりにまとめます。
入門編はこれ一つで全部学べるみたいなので、みなさんもぜひやってみてください!
cryptozombies.io
Crypto Zombieで始めるSolidity入門⑤
この記事は前回の続きです。
前回の記事はこちら↓
chanichiwasshoi.hateblo.jp
このシリーズではCrypto ZombieというSolidityの学習が出来るサイト(しかも無料で)の内容を自分なりにまとめます。
入門編はこれ一つで全部学べるみたいなので、みなさんもぜひやってみてください!
cryptozombies.io
トークンとは
トークンとは、いくつかの共通ルールに従ったスマートコントラクトのことです。全てのトークンは同じ名前の同じ関数を共有しているため、同じ方法で相互に作用することが可能になっています。
トークンの種類
トークンにはERC20トークンやERC721トークンのようにいくつかの種類があります。それぞれのトークンに特徴があり、例えばERC20トークンは通貨のような働きをし、ERC721トークンはNFTのような収集物に適しています。
コントラクトをトークンにする
自分のスマートコントラクトをトークンにするには、ただトークンを定義したコントラクトを継承すれば良い。
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
}
// これがトークン
contract Hoge is ERC721 {
function balanceOf(address _owner) public view returns (uint256 _balance) {}
function ownerOf(uint256 _tokenId) public view returns (address _owner) {}
...
}
ERC721トークンについて
contract ERC721 {
event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
function balanceOf(address _owner) public view returns (uint256 _balance);
function ownerOf(uint256 _tokenId) public view returns (address _owner);
function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;
} ERC721トークンをよく見てみると、トークンを転送する方法が2つあることが分かる。
一つが、「transfer」を使って単純に送る方法。
もう一つが、「approve」によって受取先アドレスにトークンの受け取りを許可し、「takeOwnership」で実際に受け取り者が受け取るという2つの段階を踏む方法。
2つ目の方法を使うことで正しく送り先アドレスにトークンを送ることが出来る(送り先を間違えるなどのミスが減る)。
多重継承
solidityは多重継承に対応している。
contract GreatestContract is Hoge, Fuga {}
SafeMath
一般的なプログラミング言語では、変数の値がオーバーフローしてしまうと、数値が0に戻ってしまう。(例: ffff + 0001 = 0000)
これは、Solidityでも例外ではなく実際にお金の動くことの多いブロックチェーンでは重大な問題に発展する可能性がある。
そういったオーバーフロー、アンダーフローを防ぐためにあるのがSafeMathというライブラリだ。SafeMathでは、オーバーフローが起きたときにエラーを起こして処理を止めてくれる。
ライブラリの使い方
ライブラリを使うには以下のようにする。
import "safemath.sol"; using SafeMath for uint256; uint256 a = 5; uint256 b = a.add(3); // 5 + 3 = 8 uint256 c = a.sub(3); // 5 - 3 =2 uint256 d = a.mul(2); // 5 * 2 =10 uint256 e = a.div(5); // 5 / 2 = 1
Crypto Zombieで始めるSolidity入門④
この記事は前回の続きです。
前回の記事はこちら↓
chanichiwasshoi.hateblo.jp
このシリーズではCrypto ZombieというSolidityの学習が出来るサイト(しかも無料で)の内容を自分なりにまとめます。
入門編はこれ一つで全部学べるみたいなので、みなさんもぜひやってみてください!
cryptozombies.io
コントラクトでコインを扱う
Payable関数
スマートコントラクトを呼び出す人はガス代以外に、コントラクトあてに任意量のコインを送ることが出来る。
送られたコインをSolidityメソッド側で受け取るには、呼び出される関数にPayableという修飾子を付けなければならない。
function buySomething() external payable {
require(msg.value == 0.001 ether);
transferThing(msg.sender);
}送られたコインの量はmsg.valueでアクセス可能だ。
フロントエンド側からは以下のようにして呼び出す。
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})valueを見ると0.001etherが送られていることが分かるので、この関数は実行される。
Transfer関数
送られてきたコインを引き出すことができなければ、そのコインはずっとアカウントに閉じ込められたままになってしまう。コインを他の場所へ送るには以下のようにすれば良い。
function withdraw() external onlyOwner {
owner.transfer(this.balance);
}こうすることで、オーナーのアカウントに送られてきたコインを転送できる。
また、支払いが多すぎた場合に送り返す処理は以下の通りである。
uint itemFee = 0.001 ether; msg.sender.transfer(msg.value - itemFee);
- もっと読む
コメントを書く