從多個地址收集 ERC20 餘額?
我正在建構一個 dApp,其中涉及為 ERC20 存款的個人使用者創建唯一帳戶
$$ I keep the addresses and private keys in a centralized database $$,對於流行的 ERC20 代幣,如 BNB、0x 等。但是,為了發揮作用,我的 dApp 需要將所有這些餘額收集到一個中央錢包中。由於這些是 ERC20 存款而不是 ETH 存款,因此使用者錢包中沒有存放 ETH,因此無法支付用於將其 ERC20 餘額發送到中央錢包的 gas。 有誰知道這個問題的有效解決方案?我幼稚的解決方案是,每次我檢測到一個 ERC20 存入該錢包時,就將 ETH 發送到使用者的錢包中,這樣該錢包就可以支付傳輸的 gas,但這很醜陋,而且看起來像很多 gas 成本。
謝謝!
今天有兩種方法可以做到:
1.普通賬戶
生成您的使用者轉移到的錢包密鑰/地址,並且您必須向這些地址發送氣體才能轉移資金。
缺點:
- 收集資金需要您將氣體發送到這些地址,這最終會導致灰塵堆積在這些地址中
- 儲存和管理帳戶私鑰可能會成為一項挑戰
- 智能合約賬戶 =========
部署一個智能合約工廠,生成一個主“賬戶可以控制”的“接收地址”。
本質上,您有一個智能合約,可以創建儲存收到資金的新地址,並且您有一個“所有者”地址,可以呼叫一種方法來轉移資金。
有多種方法可以實現這一點:
Factory 合約可以實現所有權驗證並控制接收者,或者每個接收者可以自己進行驗證。
我能想到的最佳實施、安全性和性能方面如下:
工廠契約:
- 有一個所有者(所有者可以更改)
- 所有者可以創建新的“接收者”
- 工廠索引映射中的接收器( uint => address )
- 所有者可以從接收者契約中收取或返還資金
- 接受批量呼叫以便從多個接收方收取/退款
如果您想降低 gas 使用量並實施硬體簽名,批處理很重要
考慮使用單獨的伺服器進行簽名,您甚至可以將外部硬體錢包插入其中,或者 HSM(硬體簽名模組)。
這裡最棒的部分是你甚至可以通過這樣的方式來實現它,你可以從任何基於乙太坊的智能合約/令牌跟踪器(BNB / DAI / 等)或使用自定義呼叫者介面的任何令牌標準收集令牌(你可以呼叫您想要的任何地址的任意方法)。
無需向這些接收者發送 gas,因為所有者是“發送交易”的一方,而“接收者合約是代幣的所有者”
用於部署和與接收器互動的廉價 gas,因為您可以在網路不擁塞時執行此操作,並且能夠與 GasToken ( gastoken.io ) 集成以進一步降低您的價格。
希望這可以幫助。
範例起始程式碼
pragma solidity ^0.4.25; /* Just the interface so solidity can compile properly We could skip this if we use generic call creation or abi.encodeWithSelector */ contract ERC20 { function totalSupply() public constant returns (uint); function balanceOf(address tokenOwner) public constant returns (uint balance); function allowance(address tokenOwner, address spender) public constant returns (uint remaining); function transfer(address to, uint tokens) public returns (bool success); function approve(address spender, uint tokens) public returns (bool success); function transferFrom(address from, address to, uint tokens) public returns (bool success); event Transfer(address indexed from, address indexed to, uint tokens); event Approval(address indexed tokenOwner, address indexed spender, uint tokens); } /* Generic Receiver Contract */ contract Receiver { address public owner; constructor() public { /* Deployer's address ( Factory in our case ) do not pass this as a constructor argument because etherscan will have issues displaying our validated source code */ owner = msg.sender; } /* @notice Send funds owned by this contract to another address @param tracker - ERC20 token tracker ( DAI / MKR / etc. ) @param amount - Amount of tokens to send @param receiver - Address we're sending these tokens to @return true if transfer succeeded, false otherwise */ function sendFundsTo( address tracker, uint256 amount, address receiver) public returns ( bool ) { // callable only by the owner, not using modifiers to improve readability require(msg.sender == owner); // Transfer tokens from this address to the receiver return ERC20(tracker).transfer(receiver, amount); } // depending on your system, you probably want to suicide this at some // point in the future, or reuse it for other clients } /* Factory Contract */ contract Factory { address public owner; mapping ( uint256 => address ) public receiversMap; uint256 receiverCount = 0; constructor() public { /* Deployer's address ( Factory in our case ) do not pass this as a constructor argument because etherscan will have issues displaying our validated source code */ owner = msg.sender; } /* @notice Create a number of receiver contracts @param number - 0-255 */ function createReceivers( uint8 number ) public { require(msg.sender == owner); for(uint8 i = 0; i < number; i++) { // Create and index our new receiver receiversMap[++receiverCount] = new Receiver(); } // add event here if you need it } /* @notice Send funds in a receiver to another address @param ID - Receiver indexed ID @param tracker - ERC20 token tracker ( DAI / MKR / etc. ) @param amount - Amount of tokens to send @param receiver - Address we're sending tokens to @return true if transfer succeeded, false otherwise */ function sendFundsFromReceiverTo( uint256 ID, address tracker, uint256 amount, address receiver ) public returns (bool) { require(msg.sender == owner); return Receiver( receiversMap[ID] ).sendFundsTo( tracker, amount, receiver); } /* Batch Collection - Should support a few hundred transansfers @param tracker - ERC20 token tracker ( DAI / MKR / etc. ) @param receiver - Address we're sending tokens to @param contractAddresses - we send an array of addresses instead of ids, so we don't need to read them ( lower gas cost ) @param amounts - array of amounts */ function batchCollect( address tracker, address receiver, address[] contractAddresses, uint256[] amounts ) public { require(msg.sender == owner); for(uint256 i = 0; i < contractAddresses.length; i++) { // add exception handling Receiver( contractAddresses[i] ).sendFundsTo( tracker, amounts[i], receiver); } } }
全部
- 添加所有權變更
- 添加視圖以讀取指定跟踪器的接收器餘額
- 在需要的地方添加事件
- 在 batchCollect 中添加異常處理
流動:
- 1.部署工廠合約
- 2.呼叫Factory合約上的createReceivers
- 3.將一些測試令牌發送到receiversMap中映射的接收器
- 4.呼叫 Factory 上的 sendFundsFromReceiverTo將所述代幣移動到新地址