$smart は sushiswap の CTO joseph.ethがEIP-7702 提案を利用して BASE ネットワークに展開した Token です。
EIP=7702#
EIP-7702 の実装原理は、外部所有アカウント(EOA)が単一の取引中に一時的にスマートコントラクトアカウントの機能を持つことを可能にする革新的なメカニズムに基づいており、EOA に対して永続的な変更を加えることはありません。その核心は、contract_code フィールドを追加することで、取引の過程でそのコードを EOA に割り当て、スマートコントラクト機能を持たせることです。取引が完了すると、スマートコントラクトコードは自動的に削除され、EOA は元の状態に戻ります。
$smart を正しく受け取る方法#
$smart コントラクトの簡単な分析#
```Solidity
function prepapreClaim() external {
assembly {
tstore(0, 1)
}
}
function claim() external {
assembly {
if iszero(tload(0)) {
mstore(0x00, 0x33adb0bc) // `RIPBozo()`.
revert(0x1c, 0x04)
}
}
if (hasClaimed[msg.sender]) {
revert OnlyOneClaimPerWallet();
}
volatilityAccumulator = volatilityAccumulator + (block.timestamp - lastClaim) >> 1;
uint256 amount = (DEFAULT_AMOUNT * volatilityAccumulator) / TARGET_INTERVAL;
amount = (amount > DEFAULT_AMOUNT) ? DEFAULT_AMOUNT : amount;
uint256 supply = totalSupply();
// mintcapがすでに達成されている
if (supply == MINT_CAP) {
revert MintCapExceeded();
}
// mintcapに達することを確認
else if (supply + amount > MINT_CAP) {
amount = MINT_CAP - supply;
}
hasClaimed[msg.sender] = true;
lastClaim = block.timestamp;
_mint(msg.sender, amount);
}
```
$smart コントラクトの主な部分は prepapreClaim と claim 関数で、prepapreClaim 関数はストレージスロット 0 の値を 1 に変更し、claim 関数は tload (0) を通じてストレージスロット 0 の値を読み取ります。値が 0 の場合、claim 関数を終了します。prepapreClaim 関数と組み合わせることで、Token を成功裏に mint するためには、同一の取引内で最初に prepapreClaim 関数を呼び出してストレージスロット 0 の値を 1 に変更し、その後 claim 関数を呼び出して Token を受け取る必要があります。同一の取引内で 2 つの関数を呼び出すためには、コントラクトウォレットを使用する必要があります。EOA ウォレットでは同一の取引内で 2 つの関数を実行することはできません。
なぜコントラクトウォレットを使用して受け取ると、amount が 0 になるのか?#
この質問に答えるためには、claim 関数内で amount がどのように計算されるかを分析する必要があります。claim 関数内で amount を計算するコードは、主に以下の 4 行によって決まります。
// amountのリリース率を設定するvolatilityAccumulatorグローバル変数がvolatilityAccumulatorの値を動的に変更します
volatilityAccumulator = volatilityAccumulator + (block.timestamp - lastClaim) >> 1;
// リリース率 * デフォルトのamount数量
uint256 amount = (DEFAULT_AMOUNT * volatilityAccumulator) / TARGET_INTERVAL;
// amountがDEFAULT_AMOUNTを超える場合、amountはDEFAULT_AMOUNTに等しくなり、そうでない場合はamountに等しくなります
amount = (amount > DEFAULT_AMOUNT) ? DEFAULT_AMOUNT : amount;
lastClaim = block.timestamp;
リリース率は amount を計算するための重要なパラメータであり、コードから見るとリリース率に影響を与えるパラメータは主に volatilityAccumulator と block.timestamp によって決まります。コントラクトが展開される際のデフォルトの volatilityAccumulator は 600 であるため、実際にリリース率に影響を与えるのは block.timestamp の時間だけです。
-
通常の状況でのリリース率
lastClaim と block.timestamp の差が 1 秒であると仮定すると、volatilityAccumulator は次のように計算されます:
volatilityAccumulator = 600 + 1 >> 1; volatilityAccumulator = 601 / 2; volatilityAccumulator = 300.5;
-
極端な状況でのリリース率
ユーザーが claim 関数を呼び出し続ける場合(block.timestamp が lastClaim に等しい)、volatilityAccumulator の値は多くの呼び出しの後に 0 に近づきます。なぜなら、volatilityAccumulator はグローバル変数であり、claim 関数を呼び出すたびにその値が動的に変更されるからです。この場合、amount の計算は常に 0 になり、これが一部の人が受け取った数量が 0 である理由を説明します。
volatilityAccumulator = 0; uint256 amount = 0; amount = 0;
BASE ネットワークに展開された $smart は、実際には極端な状況に該当します。なぜなら、ボットが常にコントラクトを展開して smart を受け取っているため、volatilityAccumulator の値がほぼ 0 になっているからです。Token を得るためには、block.timestamp - lastClaim >= 1 である必要がありますが、base ネットワークのブロック生成時間は 2 秒であるため、同じブロック内で 1 回しか受け取れないと言われる理由を説明します。
このような極端な状況でのリリース率の下で、block.timestamp - lastClaim >= 1 のときに得られる Token の量を分析します:
volatilityAccumulator = 無限に近づくが、0にはならない;
// uint256 amount = (DEFAULT_AMOUNT * volatilityAccumulator) / TARGET_INTERVAL
// amountを計算するコードは、次のように近似できます
// デフォルトのDEFAULT_AMOUNTは500000000000000000000000 wei、つまり500000 etherです
// TARGET_INTERVALは600です
// amount = 500000 / 600
// amount = 833.33333333
uint256 amount = DEFAULT_AMOUNT / TARGET_INTERVAL;
受け取る成功率を上げる方法は?#
上記の分析を経て、各ブロック内で自分の取引が優先的にパッケージ化されることを確保すれば、833.33333333 Token を 100% 受け取ることができることが明確になりました。では、どのようにして自分の取引を常に最初にすることができるのでしょうか?誰かが GAS 価格を上げることを提案しましたが、私はそれについてあまり確信がありません。ブロックチェーンプロトコルの種類が多様であるため、Base ネットワークのブロックパッケージングルールには詳しくないため、単純に GAS 価格を上げることが取引の順序に影響を与えるかどうかは判断できません。
結論#
このツイートに返信を残してくれた皆さんに感謝します。
最後に以下のコードを remix で検証してください#
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract ClaimHelper {
uint256 lastClaim;
uint256 volatilityAccumulator;
uint256 public immutable DEFAULT_AMOUNT;
uint256 public immutable TARGET_INTERVAL;
uint256 public amount;
constructor(
uint256 _targetInterval,
uint256 _defaultAmount
) {
TARGET_INTERVAL = _targetInterval;
DEFAULT_AMOUNT = _defaultAmount;
lastClaim = block.timestamp;
volatilityAccumulator = _targetInterval;
}
event amountUpdata(uint256 newValue);
function claim() external {
volatilityAccumulator = volatilityAccumulator + (block.timestamp - lastClaim) >> 1;
amount = (DEFAULT_AMOUNT * volatilityAccumulator) / TARGET_INTERVAL;
amount = (amount > DEFAULT_AMOUNT) ? DEFAULT_AMOUNT : amount;
lastClaim = block.timestamp;
emit amountUpdata(amount);
}
function getVolatilityAccumulator() external view returns (uint256) {
return volatilityAccumulator;
}
function getAmount() external view returns (uint256) {
return amount;
}
}
_targetInterval と_defaultAmount を 600 に設定し、claim 関数を連続して実行した後、amount の数量を確認してください。