イーサリアムスマートコントラクト:スマートコントラクトまたは移行手法でエラーが発生した場合の対処方法

スマートコントラクトを作成する場合、ブロックチェーンにダウンロードした後は変更できなくなるため、改善を加えたり、見つかったエラーを修正したりすることはできません。 すべてのプログラムにエラーがあることは誰もが知っており、数か月前に書かれたコードに戻ると、そこで何が改善できるかを常に見つけます。 どうする? 唯一のオプションは、修正されたコードで新しい契約をアップロードすることです。 しかし、既存の契約に基づいてトークンが既に発行されている場合はどうなりますか? 移行が助けになります! 過去1年間、私はその実装のために多くの異なる技術を試し、他の大規模なブロックチェーンプロジェクトで使用されていると分析し、自分で何かを発明しました。 カットの下の詳細。









この投稿の枠組みでは、既成のスマートコントラクトのシートを提供しないことをすぐに予約しますが、さまざまなテクニックを検討して分析するだけです。 何らかの形でそれらのほとんどすべてが私が参加したプロジェクトの契約で私によって実装されており、多くは私のGitHubから取ることができます。



ERC20準拠の契約からの移行



ブロックチェーンに既にアップロードされている元の契約に移行を支援する特別なメカニズムが含まれていない場合、つまり、最も単純で最も一般的なケースから検討を開始します。 実際、定期的なERC20互換の契約があります。 最初の契約から有用なものは、移行中に誰も忘れていないことを確認するために発行されたすべてのトークン所有者の残高とトークンの合計数だけです。



contract ERC20base { uint public totalSupply; function balanceOf(address _who) public constant returns(uint); }
      
      





残念ながら、ERC20互換コントラクトのインターフェースでは、すべてのトークン所有者のリストを見つけることができないため、移行中に、たとえばetherscan.ioからダウンロードするなどして、他のソースから所有者の完全なリストを見つける必要があります。 移行契約の例を次のリストに示します。



 contract NewContract { uint public totalSupply; mapping (address => uint) balanceOf; function NewContract(address _migrationSource, address [] _holders) public { for(uint i=0; i<_holders.length; ++i) { uint balance = ERC20base(_migrationSource).balanceOf(_holders[i]); balanceOf[_holders[i]] = balance; totalSupply += balance; } require(totalSupply == ERC20base(_migrationSource).totalSupply()); } }
      
      





契約デザイナーは、etherscan.ioを介して手動でアンロードされたトークン保有者のリストと同様に、ソースERC20準拠契約のアドレスをパラメーターとして受け取ります。 コンストラクターの最後の項では、移行後にトークンの数が変更されていないこと、したがってトークンホルダーが忘れられていないことを確認することに注意してください。 このような移行は、トークンホルダーの数が少なく、すべてのトークンのサイクルが1つのトランザクション内で可能な場合にのみ可能であることに留意する必要があります(1つのトランザクションに対してイーサリアムで設定されたガス制限)。 ただし、トークンホルダーの数が1つのトランザクションで移行できない場合、この機能は必要に応じて何度でも呼び出すことができる別の関数に配置する必要があり、この場合の契約は次のようになります。



 contract NewContract { uint public totalSupply; mapping (address => uint) balanceOf; address public migrationSource; address public owner; function NewContract(address _migrationSource) public { migrationSource = _migrationSource; owner = msg.sender; } function migrate(address [] _holders) public require(msg.sender == owner); for(uint i=0; i<_holders.length; ++i) { uint balance = ERC20base(_migrationSource).balanceOf(_holders[i]); balanceOf[_holders[i]] = balance; totalSupply += balance; } } }
      
      





このコントラクトのコンストラクターでは、元のコントラクトのアドレスが記憶され、所有者フィールドもコントラクトの所有者のアドレスを記憶するように初期化されます。そのため、彼だけがmigrate()関数を呼び出す権利を持ちます。



このソリューションの欠点は次のとおりです。



  1. 古いスマートコントラクトでは、トークンは所有者に残り、新しいスマートコントラクトでは、残高は単純に複製されます。 これがどれほど悪いかは、トークン販売契約またはプロジェクトのトークン所有者に対する義務の範囲を説明する他の文書の作成方法、および「重複」を作成した後の義務が倍増するかどうかによって異なります。
  2. 移行には独自のガスを費やしますが、これは一般的に論理的です 移行を行うために発明したものであり、いずれにしてもユーザーに不便をかけますが、スマートコントラクトアドレスをウォレットで古いものから新しいものに書き換える必要があるという事実に限定されます。
  3. 移行の過程で、確かに1つのトランザクションに収まらない場合、所有者のアドレス間でトークン転送が発生する可能性があるため、新しい所有者が追加され、既存の所有者の残高が変更される可能性があります。


しかし、残念ながら、それについては何もできません。より快適で便利な移行のために、元の契約でいくつかの補助手段を提供する必要があります。



クラウドセールステージ間の移行



現代のICOの世界では、資金調達のさまざまな段階で別々の契約を交わし、発行されたトークンを新しい段階の新しい契約に移行する場合、慣行は非常に一般的です。 もちろん、これは上記で検討したように実行できますが、移行する必要があることが確実にわかっている場合は、すぐに生活を簡素化してみませんか? これを行うには、公開フィールドを入力するだけです



  address [] public holders;
      
      





すべてのトークン所有者をこのフィールドに追加する必要があります。 契約が既にコレクションの初期段階にある場合、所有者がトークンを移動できるようにする場合、つまり transfer()を実装します。たとえば、次のような配列が更新されていることを確認する必要があります



  mapping (address => bool) public isHolder; address [] public holders; …. if (isHolder[_who] != true) { holders[holders.length++] = _who; isHolder[_who] = true; }
      
      





これで、受け入れ契約の側では、以前に検討したものと同様の移行テクノロジーを使用できますが、配列をパラメーターとして転送する必要はありません。元の契約で既に準備された配列を参照するだけで十分です。 また、トランザクションごとのガスの制限により、配列のサイズによって1つのトランザクションでの反復が許可されない可能性があることを覚えておく必要があります。したがって、この内で処理する配列の初期要素と最終要素の数という2つのインデックスを受け取るmigrate()関数を提供する必要がありますトランザクション。



このソリューションの欠点は、通常は以前のものと同じですが、etherscan.ioを介してトークンホルダーのリストをアップロードする必要がなくなりました。



ソーストークンの書き込みを伴う移行



それでも、新しいスマートコントラクトでのトークンの複製ではなく移行について話しているため、新しいコントラクトでコピーを作成するときは、元のコントラクトでトークンを破棄(書き込み)する問題に注意する必要があります。 明らかに、スマートコントラクトの所有者でさえも、他の所有者のトークンを焼き付けることを可能にするスマートコントラクトに「穴」を残すことは受け入れられません。 このようなスマートな契約は詐欺にすぎません! 所有者のみがトークンに対してこのような操作を実行できるため、所有者自身が移行を実行する必要があります。 この場合、スマートコントラクトの所有者は、この移行のみを開始できます(スマートコントラクトを移行状態にします)。 GOLEMプロジェクトで出会ったそのような移行の実装例(投稿の最後にあるgithubへのリンク)、それをいくつかのプロジェクトで実装しました。



元のコントラクトでは、MigrationAgentインターフェイスを定義します。これは、移行が実行されるコントラクトに後で実装する必要があります。



 contract MigrationAgent { function migrateFrom(address _from, uint256 _value); }
      
      





初期トークンコントラクトでは、次の追加機能を実装する必要があります。



 contract TokenMigration is Token { address public migrationAgent; // Migrate tokens to the new token contract function migrate() external { require(migrationAgent != 0); uint value = balanceOf[msg.sender]; balanceOf[msg.sender] -= value; totalSupply -= value; MigrationAgent(migrationAgent).migrateFrom(msg.sender, value); } function setMigrationAgent(address _agent) external { require(msg.sender == owner && migrationAgent == 0); migrationAgent = _agent; } }
      
      





したがって、元のスマートコントラクトの所有者はsetMigrationAgent()を呼び出して、移行が実行されるスマートコントラクトのアドレスをパラメーターとして渡す必要があります。 その後、元のスマートコントラクトのすべてのトークンホルダーは、migrate()関数を呼び出す必要があります。これにより、元のスマートコントラクトのトークンが破棄され、新しいトークンが新しいトークンに追加されます(新しいコントラクトのmigrateFrom()関数を呼び出して)。 新しいコントラクトには、たとえば次のように、MigrationAgentインターフェイスの実装が実際に含まれている必要があります。



 contract NewContact is MigrationAgent { uint256 public totalSupply; mapping (address => uint256) public balanceOf; address public migrationHost; function NewContract(address _migrationHost) { migrationHost = _migrationHost; } function migrateFrom(address _from, uint256 _value) public { require(migrationHost == msg.sender); require(balanceOf[_from] + _value > balanceOf[_from]); // overflow? balanceOf[_from] += _value; totalSupply += _value; } }
      
      





このソリューションではすべてが完璧です! さらに、ユーザーはmigrate()関数を呼び出す必要があります。 いくつかのウォレットのみが関数呼び出しをサポートし、原則としてそれらは最も便利ではないという事実により、状況は非常に複雑です。 したがって、あなたのトークンの保有者の中に暗号学だけでなく、普通の人間もいるなら、何らかのミストをインストールする必要があることを彼らに説明するときに彼らはあなたを呪うでしょうパラメータなしでも)。 どうする?



そして、あなたはそれを非常に簡単に行うことができます! 結局のところ、どんな暗号通貨ユーザーでも、最も初心者でも、1つのことをうまく行うことができます-自分のアドレスから他の人に暗号を送信します。 したがって、このアドレスをスマートコントラクトのアドレスとし、「移行」モードでのフォールバック機能は単にmigrate()を呼び出します。 したがって、トークン所有者が移行を完了するには、奇跡を起こすために「移行」モードでスマートコントラクトのアドレスに少なくとも1ウェイを転送するだけで十分です。



 function () payable { if (state = State.Migration) { migrate(); } else { … } }
      
      





おわりに



検討されたソリューションは、特定の実装のバリエーションが可能ですが、トークンを移行するすべての可能な方法を概念的にカバーします。 「蒸留容器」アプローチには特別な注意が必要です(記事の最後にあるリンク)。 移行のアプローチに関係なく、スマートコントラクトはEthereum仮想マシン内で実行されるプログラムだけでなく、一種の疎外された独立したコントラクトであり、移行ではこのコントラクトの条件を変更することを前提としています。 トークンの所有者は、トークンを購入するときに締結した契約の条件を変更したいのですか? これは実際には良い質問です。 また、トークン保持者に新しい契約に「移動」するかどうかを「尋ねる」という非常に良い方法があります。 PROVERプロジェクトのスマートコントラクトに実装したのは、投票による移行の実装でした。コントラクトのテキストは、GitHubにあります。 そしてもちろん、 PROVERプロジェクトのICOにご参加ください。



これらすべてが誰かにとって有用で必要であることを願っています:)。



便利なリンク






All Articles