memo チャリティDAOを作りたい ~Token Based Voting for Charity DAO(dapps)~
トークンベースのクォーラム投票
トークンベースのクォーラムは、最も基本的なDAO投票メカニズムの 1 つです。提案が可決されるには、一定数の DAO メンバーが投票プロセスに参加する必要があります。しきい値が満たされている場合、最も多くの票を獲得した決定が勝ちます。しきい値に達しないということは、提案が失敗したことを意味します。
トークンベースの定足数投票は、メンバーの積極的な参加を促し、大衆の意見を考慮しようとしますが、いくつかの課題があります。多くの場合、適切な定足数を特定することは困難です。より多くのメンバーに投票を求めることは、参加レベルが低いために提案の大部分が失敗することを意味する可能性があります。ただし、定足数を低く設定すると、悪い慣行のリスクが生じ、提案を非常に簡単に通過させることができ、DAO が間違った方向に発展する可能性があります。
Permissioned Relative Majority
許可された相対的多数決のDAOの投票メカニズムでは、重要な要素は、何人の投票者が提案に「賛成」「反対」の投票をしたかということです。最低投票数は定められておらず、1人のメンバーでも意思決定に単独で参加することが可能です。投票プロセスが簡単でわかりやすいため、コストや負担が少なくて済む。しかし、この仕組みでは、一人のDAOメンバーが大きな力を持ちすぎて、DAOの資金をどのように管理するかを選択することができます。さらに、この仕組みでは、提案の通過は他のメンバーからあまり注目されない非常に簡単なプロセスであるため、リスクの高い取り組みとなります。
Rage Quitting
Rage Quittingの投票メカニズムは、DAOの投票プロセスにおけるセキュリティを高めるためにスポンサーシップが活用された一例である。この仕組みは、相対的多数派の課題に対する解決策を提供する可能性がある。提案が投票される前に、それはメンバーによってスポンサーされなければならない。多数決で承認された場合、その提案は猶予期間に入り、投票者は再考して投票やDAOからの支持を撤回することができます。この段階で十分な支持を得られなくなった提案は破棄される。
二次 DAO 投票メカニズム
二次 DAO 投票メカニズムの下では、議決権は財政力に直結します。すべてのメンバーは、提案に対して繰り返し投票する権利を持ちます。投票のコストは、メンバーが取得したい票数の 2 乗です。たとえば、1 回の投票で 1 トークンが必要な場合、2 票で 4 トークン、3 票で 9 トークンが必要です。これにより、メンバーは提案、特に非常に情熱を注いでいる決定をどれだけ強く信じているかを示すことができます。
この投票モデルは、相対的多数決のリスクに対する解決策を提供し、DAO コミュニティが特定のトピックについていかに強い意見を持っているかを明確に示しています。
有罪判決投票
有罪判決投票は、コミュニティの集計された好みに基づいており、時間をユーティリティとして使用します。メンバーは進行中のさまざまな提案に投票することができ、同じ投票が長く続くほど、投票の力は強くなります。投票効用の増加は、設定された最大点に近づくにつれて徐々に遅くなります。投票者はいつでも自分の投票を変更できますが、その場合、以前の投票の投票効用は最終的に減少します。
このメカニズムは、投票者が提案にどの程度関心を持っているか、またおそらく彼らの意見が内的または外的要因によってどのように影響されているかを示すのに最適な方法です。提案を可決するために過半数の投票は必要ありませんが、コミュニティの信念が意思決定の核となります。また、新しい DAO メンバーが DAO プロトコルで強力になりすぎるリスクを排除する効率的な方法でもあります。
ホログラフィック コンセンサス DAO 投票メカニズム
ホログラフィック コンセンサス DAO 投票メカニズムは、DAO で可決される可能性が最も高い提案を選別して注目を集めるように設計されています。この投票モデルは、分散型組織におけるガバナンスのスケーラビリティと回復力の問題を解決することを目的としています。このメカニズムの下で、メンバーは提案が成功するか失敗するかを予測し、トークンを使用して成功すると信じているものに賭けることができます。予測が正しければ、予測者はトークンの形で報酬を受け取り、失敗するとトークンを失います。
マルチシグ投票
マルチシグ投票は、組織内の中央権限と分散化の間のバランスを作成することを目的とした DAO 投票メカニズムです。このモデルでは、DAO メンバーは提案に署名する権限を持ち、中央の事前に決められた委員会が提案された提案に対する投票を実行します。このモデルは最速の投票メカニズムの 1 つであり、緊急の行動が DAO の存続の鍵となる場合に適している可能性があります。
「意思決定のスピード」と「中央集権度」は反比例するので、そのDAOにおいての意思決定スピードはどれくらい必要なのか?を定義してからアルゴリズムを決定するのが正しそう。
また、参加者へのインセンティブは必要なのか?も定義すべきだね。
チャリティDAOを作るに当たって、ガバナンストークンと募金を交換するって形になるんだろうなー
Astarを使ってマルチチェーンでやりたいんだけど、ガバナンストークンとの交換レートが1つ難しいポイントになりそう
ガバナンスのコントラクトと、募金~送金のコントラクトの2つに分けられそう
有名なDAOたち
DAOhaus : DAOhaus は、DAO を起動して実行するためのノーコード プラットフォームです。コミュニティによって所有および運営されています。独自の DAO を開始したり、活気に満ちた風景を探索したりすることに興味がある場合は、もう探す必要はありません。
MakerDAO : 世界初の公平なステーブル コインである DAI を導入したプロトコルに貢献したい場合は、Maker プロトコルの変更に 投票することでガバナンスに参加できます。
RaidGuild : このサービスベースの DAO は、MetaCartelネットワークに由来し、Web3 の世界に深く定着しています。開発者、マーケティング、またはデザインのスキルをギルドに提供しようとしている場合、ギルドは製品の悪魔 を殺し続けるための質の高い才能を探しています.
Proof Of Humanity : このシビル抵抗力のある人間のレジストリは、社会的検証とKleros の裁判所を使用して、ユニバーサル ベーシック インカム (UBI) トークンを検証済みの人間にオンチェーンで配布します。司法革命に参加したい場合は、この民主的な DAOから始めてください。
Opolis : このメンバー所有のデジタル雇用協同組合は、独立した労働者に福利厚生と共有サービスを提供します。仕事の未来に情熱を持っていて、それがどのようなものかについて発言したい場合は、ここに参加してください。
BanklessDAO : Web3 という言葉を広め、コンテンツを通じて大衆を教育することに興味がありますか? このメディア中心の DAO に興味があるかもしれません。詳細については、こちらをご覧ください。
MolochDAO : この OG DAO は、イーサリアム エコシステムを前進させるための助成金を授与します。運営メンバーになり、このグループに貢献したい場合は、この申請書に記入してください。
MakerDAOしか知らなかった。BTCはどこ行ったんだ?
moralis、お前 DAOのコントラクト例出してくれてるのか、、、!神
募金は
- 何円送金するか
- どの企業に送金するか
を意思決定しなきゃいけない。しかも災害時なので意思決定スピードが大事。
ある程度中央集権になることは許容しなければいけない。
というか、募金の貢献度に応じた適切な分配よりも、意思決定が止まらないことが重要だな。
ENSってDAOなんだ、知らんかった。
この形態の企業体の法的地位は一般的に不確実であり、法域によって異なる場合があります。2017 年、米国証券取引委員会は、DAOは未登録証券の違法な提供とみなされる可能性があるとの判決を下しました。
それにもかかわらず、ワイオミング州は2021 年に DAO を法人として認めた最初の米国の州となり、 CryptoFed DAO はそうした最初の企業体でした。DAO は法的地位を持たない法人として機能することができ、一般的かつ法的にジェネラル パートナーシップと呼ばれます。
このパートナーシップ アプローチにより法的な承認が得られる可能性がありますが、DAO と従来の (規制対象の) 金融機関との間のインターフェースに参加している既知の参加者または個人は、多数の法律に違反した場合、規制措置を受ける可能性があります。
これは忘れないようにしませう。
ただ、規制側が変わっていく、ポジティブな方向には動くだろうし、これを理由に開発を技術を止める必要はない。
日本のDAO。
Ninja DAO
國光DAO
和組DAO
SUPER SAPIENSS
MZ CLUB
HENKAKU Discord Community
必ずしもコントラクトがあってガバナンストークンが存在しているわけではなさそう。
コミュニティが近いかも。
募金、送金だけ作ったやつ
openzepplinがgovernanceについてのコントラクト出していた
時間ではなく、ブロック数単位で遅延を測っているな
コンパウンドってのがあるらしい
Compoundはイーサリアム上で稼働している分散型取引所の1つで、ユーザー同士で仮想通貨の貸し借りをすることができるレンディングプラットフォームです。
CompoundにおけるCOMPを使ったガバナンスでは、全ての提案に対して3日間の投票期間が設けられています。提案の例としては、Compoundで使用可能な仮想通貨の種類を決めたり、仮想通貨ごとの利率を決めるパラメータの数値を決めたりと多岐に及びます。それぞれの提案は、過半数および最低40万の賛成票を集めた場合にのみ承認され、その後専用のスマートコントラクトに追加されてから2日後に開発が始まるようになっています。
The Top 7 Governance Tokens in Crypto
- AAVE
- Maker.
- Decred.
- Compound.
- Uniswap.
- PancakeSwap.
- eCash.
makerとuniswap, pancakeは聞いたことあったけど他は知らんかった
onlyGovernance()
モディファイア
機能を制限して、ガバナンス提案を通じてのみ実行できるようにします。たとえば、 のガバナンス パラメーター セッターは、GovernorSettingsこの修飾子を使用して保護されます。
ガバナンス実行アドレスは、知事自身のアドレスとは異なる場合があります。たとえば、タイムロックである可能性があります。これは、モジュールをオーバーライドすることでカスタマイズできます_executor。エグゼキューターは、ガバナーの関数の実行中にのみこれらの関数を呼び出すことができexecute、それ以外の状況では呼び出すことができません。したがって、たとえば、追加のタイムロック プロポーザーは、ガバナンス プロトコルを経由せずにガバナンス パラメーターを変更することはできません (v4.6 以降)。
onlyOwner的な、proposalとして通ったやつじゃないと発火できないようなメソッド制限みたいなのがかけられる
hashProposal(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) → uint256
プロポーザル ID は、ABI でエンコードされたtargets配列、values配列、calldatas配列、および descriptionHash (説明文字列の keccak256 ハッシュである bytes32) をハッシュすることによって生成されます。この提案 ID は、イベントの一部である提案データから生成できますProposalCreated。提案が提出される前に、事前に計算することもできます。chainId とガバナー アドレスは、提案 ID 計算の一部ではないことに注意してください。したがって、同じ提案 (同じ操作と同じ説明) は、複数のネットワークにまたがる複数のガバナーで送信された場合、同じ ID を持つことになります。これはまた、同じ操作を (同じガバナーで) 2 回実行するには、提案者が提案 ID の競合を避けるために説明を変更する必要があることを意味します。
ハッシュ化する関数かな?
GovernorVotesComp
GovernorComp トークンからの投票重み抽出の拡張。
Comp専用の拡張もあるっぽい。OpenzepplinはCompと共同でこのモジュール作ったって書いてあったし、その影響ですな。
Timelockとか遅延とかどういう意味だろうって調べた。
そもそも遅延が存在する意味👇
その理由は、投票期間が終了してから提案を実行できるようになるまでに遅延を設定するためです。提案がシステムに大幅な変更を加える可能性があるとしましょう。遅延を設定することで、ユーザーは提案が実行されることを知り、準備を整えることができます。たとえば、彼らは提案の後、コインは無価値になると考えているので、売却する時間があります。遅延がなければ、ユーザーは提案が通過するかどうかを確実に知ることができず、それに応じて準備することもできません.
quorumとは分散システムにおいて、分散トランザクションが処理を実行するために必要な最低限の票の数である。
from wiki
GovernorCompatibilityBravo
よくわからん
import "@openzeppelin/contracts/governance/utils/Votes.sol";
代表者がいない場合、自分自身に委任する必要があるらしい
デリゲートの投票の完全な履歴はチェーン上で追跡されるため、ガバナンス プロトコルは投票が特定のブロック番号で配布されたと見なされ、フラッシュ ローンや二重投票から保護されます。オプトイン委任システムにより、この履歴追跡のコストはオプションになります。
誰に投票した、とかの情報はオンチェーンになっちゃうの仕方ないけどプライバシー問題あるなー
Operation:
タイムロックの対象となるトランザクション (または一連のトランザクション)。提案者によってスケジュールされ、実行者によって実行される必要があります。タイムロックは、提案と実行の間の最小限の遅延を強制します (操作のライフサイクルを参照)。操作に複数のトランザクションが含まれる場合 (バッチ モード)、それらはアトミックに実行されます。操作は、そのコンテンツのハッシュによって識別されます。
Operation status:
- Unset: タイムロック メカニズムの一部ではない操作。
- Pending:タイマーが期限切れになる前にスケジュールされた操作。
- Ready: タイマーの期限が切れた後、スケジュールされた操作。
- Done:実行された操作。
Predecessor: 操作間の (オプションの) 依存関係。操作は別の操作 (その前の操作) に依存することができ、これら 2 つの操作の実行順序が強制されます。
Role:
- Admin: 管理者:提案者と実行者の役割の付与を担当するアドレス (スマート コントラクトまたは EOA)。
- Proposer: スケジューリング (およびキャンセル) 操作を担当するアドレス (スマート コントラクトまたは EOA)。
- Executor:タイムロックの期限が切れた後の操作の実行を担当するアドレス (スマート コントラクトまたは EOA)。この役割をゼロアドレスに付与して、誰でも操作を実行できるようにすることができます。
executor、誰でもにしちゃった方が一見綺麗ではあるなー。
あるいは、エグゼキューターの役割をゼロアドレスに付与することにより、タイムロックが期限切れになると、任意のアドレスが提案を実行できるようにすることができます。
Adminは提案者ロール与えられるらしいけど、全員提案できるようにしたいなー
ガバナンストークンの保有量が一定以上達したらロールがclaimできるようにするかなー?
定義ここにある。結局ソースコード読むのが1番理解深まる説。
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
require(
getVotes(_msgSender(), block.number - 1) >= proposalThreshold(),
"Governor: proposer votes below proposal threshold"
);
// ここでハッシュ化している
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
require(targets.length == values.length, "Governor: invalid proposal length");
require(targets.length == calldatas.length, "Governor: invalid proposal length");
require(targets.length > 0, "Governor: empty proposal");
// proposalsって配列が存在していて、ハッシュ化した数値をindexとして提案を取得している
// proposalsに既存で存在していなかった場合どうなるんや?
// proposalCoreって型はどんな型だろう?createdした時と同じ型かな?
ProposalCore storage proposal = _proposals[proposalId];
// statusが有効かどうか確認してる
require(proposal.voteStart.isUnset(), "Governor: proposal already exists");
// --------------期限の定義-----------------
uint64 snapshot = block.number.toUint64() + votingDelay().toUint64();
uint64 deadline = snapshot + votingPeriod().toUint64();
proposal.voteStart.setDeadline(snapshot);
proposal.voteEnd.setDeadline(deadline);
// --------------期限の定義-----------------
emit ProposalCreated(
proposalId,
_msgSender(),
targets,
values,
new string[](targets.length),
calldatas,
snapshot,
deadline,
description
);
return proposalId;
}
struct ProposalCore {
Timers.BlockNumber voteStart;
Timers.BlockNumber voteEnd;
bool executed;
bool canceled;
}
mapping(uint256 => ProposalCore) private _proposals;
らしい
ハッシュ化してindex取得しているけど、ハッシュ元(proposeの引数)が同じだったらindexは同じ。
setDeadlineで上書きすることによって既存のデータでも再度発行できるような形になっているようだね。
function execute(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public payable virtual override returns (uint256) {
uint256 proposalId = hashProposal(targets, values, calldatas, descriptionHash);
ProposalState status = state(proposalId);
require(
status == ProposalState.Succeeded || status == ProposalState.Queued,
"Governor: proposal not successful"
);
_proposals[proposalId].executed = true;
emit ProposalExecuted(proposalId);
_beforeExecute(proposalId, targets, values, calldatas, descriptionHash);
_execute(proposalId, targets, values, calldatas, descriptionHash);
_afterExecute(proposalId, targets, values, calldatas, descriptionHash);
return proposalId;
}
executeするときも、proposeした時と同じ引数定義しなきゃいけないのね。
これ結局execute中の_executeで実行しているな。
function _execute(
uint256, /* proposalId */
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 /*descriptionHash*/
) internal virtual {
string memory errorMessage = "Governor: call reverted without message";
for (uint256 i = 0; i < targets.length; ++i) {
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
Address.verifyCallResult(success, returndata, errorMessage);
}
}
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
ここむずいっすねー
提案しているメソッドを呼び出しているだけだと思うけど。
アドレスにcallってメソッドが生えてて、引数としてvalueとcalldata呼んでいるっぽい。
- 括弧はいくつの引数を取ることができますか? また、それらは何を意味しますか? そして、それに引数を提供する必要がありますか、それとも空のままにできますか?
call メソッドの角かっこ、つまり:
addr.call{value: msg.value, gas: 5000}(/** I am talking about this bracket*/);
シングルバイトの引数が必要です。したがって、メソッドを渡したくない場合はabi.encodeWithSignature()、空の文字列を渡す必要があります。そうしないと、コンパイラがエラーをスローします。
addressのメンバー
- balanceとtransfer
- send
- call、delegatecallおよびstaticcall
これらの関数はすべて低レベルの関数であり、注意して使用する必要があります。具体的には、不明なコントラクトは悪意のあるものである可能性があり、それを呼び出すと、そのコントラクトに制御が渡され、コントラクトにコールバックされる可能性があるため、呼び出しが戻ったときに状態変数が変更されることに備えてください。他のコントラクトと対話する通常の方法は、コントラクト オブジェクトで関数を呼び出すことです ( x.f())。
call関数には送信するガス量に制限はありませんが、transfer関数とsend関数は送信するガス量が2300ガスと決められています。そのため、「ストレージへの書き込み」や「コントラクトの作成」といった多くのガスが必要な処理ができません。
重要。
ここでのcallは low-level-callと呼ばれているらしい。
transferもlow-level-callで中は実装されてそう、知らんけど
one.deposit{value: 10, gas: 100000}();
👇
(bool success, ) = _contractOne.call{value: 10, gas: 100000}("");
👇
(bool success, ) = _contractOne.call{value: 10, gas: 100000}(payload);
この書き換えわかりやすいなー。depositが低レベルな書き方に変わっていってる
proposeの呼び出し方
提案を作成する
ガバナンス トレジャリーからの ERC20 トークンの形で、チームに助成金を与える提案を作成したいとしましょう。この提案は、ターゲットが ERC20 トークンであり、calldata がエンコードされた関数呼び出しでありtransfer(<team wallet>, <grant amount>)、0 ETH が添付されている単一のアクションで構成されます。
通常、提案は Tally や Defender などのインターフェースを使用して作成されます。ここでは、Ethers.js を使用してプロポーザルを作成する方法を示します。
まず、提案アクションに必要なすべてのパラメーターを取得します。
const tokenAddress = ...;
const token = await ethers.getContractAt(‘ERC20’, tokenAddress);
const teamAddress = ...;
const grantAmount = ...;
const transferCalldata = token.interface.encodeFunctionData(‘transfer’, [teamAddress, grantAmount]);
実際のチェーンだと上手く行くみたい。hardhatだと死んでいるっぽい
このproposeの定義をoverwriteしてrequire挟んでエラーが出ている部分を見つけてみた。
function propose(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
string memory description
) public virtual override returns (uint256) {
// getVotes(_msgSender(), block.number - 1) >= proposalThreshold(),
// 元々は上記の記述だったけど右辺と左辺分けた
uint256 voted_num = getVotes(msg.sender, block.number - 1);
uint256 pT = proposalThreshold();
require(voted_num >= pT,"Governor: proposer votes below proposal threshold");
uint256 proposalId = hashProposal(targets, values, calldatas, keccak256(bytes(description)));
....
}
uint256 voted_num = getVotes(msg.sender, block.number - 1);
において
Transaction reverted: function selector was not recognized and there's no fallback function
のエラーが出ていた
getVotesが削除された?
function _castVote(
uint256 proposalId,
address account,
uint8 support,
string memory reason,
bytes memory params
) internal virtual returns (uint256) {
ProposalCore storage proposal = _proposals[proposalId];
require(state(proposalId) == ProposalState.Active, "Governor: vote not currently active");
uint256 weight = _getVotes(account, proposal.voteStart.getDeadline(), params);
_countVote(proposalId, account, support, weight, params);
if (params.length == 0) {
emit VoteCast(account, proposalId, support, weight, reason);
} else {
emit VoteCastWithParams(account, proposalId, support, weight, reason, params);
}
return weight;
}
castVote時にunknown proposal idってことはproposalがそもそも失敗している?
function state(uint256 proposalId) public view virtual override returns (ProposalState) {
ProposalCore storage proposal = _proposals[proposalId];
if (proposal.executed) {
return ProposalState.Executed;
}
if (proposal.canceled) {
return ProposalState.Canceled;
}
uint256 snapshot = proposalSnapshot(proposalId);
if (snapshot == 0) {
revert("Governor: unknown proposal id");
}
....
的な感じでsnapshot == 0だから引っかかっている
get_Proposalで取得できているが、castVoteでunknown idと表示されてしまう。
function proposalSnapshot(uint256 proposalId) public view virtual override returns (uint256) {
return _proposals[proposalId].voteStart.getDeadline();
}
これが0なのがエラーの原因

良き画像
from https://ethereum.stackexchange.com/questions/130880/governor-proposal-not-successful
ここに問題があります。投票期間が終了するのを待つために正しい量のブロックをマイニングしていません。提案をキューに入れる前にこれを試してください await network.provider.send('hardhat_mine', ["0x284696"]
hardhatではマイニングをしないといけなさそう。
時間いじる系helper
getVotesで
function selector was not recognized and there's no fallback function
のエラーが出る。
実際の実装を見てみる
function _getVotes(
address account,
uint256 blockNumber,
bytes memory /*params*/
) internal view virtual override returns (uint256) {
return token.getPastVotes(account, blockNumber);
}
tokenの関数としてgetPastVotesが呼び出されている。getPastVotesはerc20Votesの関数。
使用しているガバナンストークンがシンプルなerc20トークンで、erc20Votesを導入していなかった。
contract Token is ERC20 {
👇
contract Token is ERC20Votes {
で解決
timer
gas feeのちゃんとした理解をしたい
gasはETH上の計算量の単位
ガスとは、イーサリアム ネットワーク上で特定の操作を実行するために必要な計算量を測定する単位を指します。 各イーサリアム トランザクションは、実行に計算リソースを必要とするため、各トランザクションには手数料が必要です。
タイムロックは、所定の時間が経過した後に別のスマート コントラクトの関数呼び出しを遅らせるスマート コントラクトです。
EOAとは
ビットコインがウォレットに紐づくBTCの金額をUTXOによって算出している一方、イーサリアムはアカウントという仕組みを構築しています。イーサリアムには、外部所有アカウント(EOA:Externally Owned Account)とコントラクトアカウントの2種類が存在します。我々は普段、ウォレットを使って他者へイーサを送金していますが、このウォレットに紐づくアカウントがEOAです。
秘密鍵によって制御され、「0x」で始まるウォレットアドレスを持つ仮想通貨ウォレットだと認識されているものの実態になります。これに対して、スマートコントラクトの実行コードを有しているのがコントラクトアカウントです。秘密鍵を有しておらず、スマートコントラクトによって制御されます。
これ見ながらなら大まかな流れわかるね
propose -> castVote -> queue -> execute的な
0 Pending
1 Active
2 Canceled
3 Defeated
4 Succeeded
5 Queued
6 Expired
7 Executed
既存のオンチェーンガバナンスシステムSnapShot
getProposal的な
function hashProposal(
address[] memory targets,
uint256[] memory values,
bytes[] memory calldatas,
bytes32 descriptionHash
) public pure virtual override returns (uint256) {
return uint256(keccak256(abi.encode(targets, values, calldatas, descriptionHash)));
}
これの逆をやってIDからcalldataとかを取得したいな
tally
txnのargをdecodeすることでcalldata value targetsなどを取得する方向
投稿した👇

