我是初学者,我一直在探索 ERC20 代币。几天以来,我一直在寻找解决方案,但徒劳无功。
问题如下。我正在创建一个符合 ERC20 协议的合约。我想以 oracle 查询的形式添加额外的功能。即,我想使用像“Oraclize”这样的服务来获取一些外部数据,返回结果。根据结果,我想转移一些代币或不转移。
1) 我一直在使用的示例代币合约如下。这是来自 CryptoPunks 的合同(https://github.com/larvalabs/cryptopunks/blob/master/contracts/CryptoPunksMarket.sol):
pragma solidity ^0.4.18;
contract CryptoTokensMarket {
address owner;
string public standard = 'CryptoTokens';
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
uint public nextTokenIndexToAssign = 0;
bool public allTokensAssigned = false;
uint public tokensRemainingToAssign = 0;
//mapping (address => uint) public addressToTokenIndex;
mapping (uint => address) public tokenIndexToAddress;
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
struct Offer {
bool isForSale;
uint tokenIndex;
address seller;
uint minValue; // in ether
address onlySellTo; // specify to sell only to a specific person
}
struct Bid {
bool hasBid;
uint tokenIndex;
address bidder;
uint value;
}
// A record of tokens that are offered for sale at a specific minimum value, and perhaps to a specific person
mapping (uint => Offer) public tokensOfferedForSale;
// A record of the highest token bid
mapping (uint => Bid) public tokenBids;
mapping (address => uint) public pendingWithdrawals;
event Assign(address indexed to, uint256 tokenIndex);
event Transfer(address indexed from, address indexed to, uint256 value);
event TokenTransfer(address indexed from, address indexed to, uint256 tokenIndex);
event TokenOffered(uint indexed tokenIndex, uint minValue, address indexed toAddress);
event TokenBidEntered(uint indexed tokenIndex, uint value, address indexed fromAddress);
event TokenBidWithdrawn(uint indexed tokenIndex, uint value, address indexed fromAddress);
event TokenBought(uint indexed tokenIndex, uint value, address indexed fromAddress, address indexed toAddress);
event TokenNoLongerForSale(uint indexed tokenIndex);
/* Initializes contract with initial supply tokens to the creator of the contract */
function CryptoTokensMarket() payable {
// balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
owner = msg.sender;
totalSupply = 10000; // Update total supply
tokensRemainingToAssign = totalSupply;
name = "CRYPTOTokenS"; // Set the name for display purposes
symbol = "Ͼ"; // Set the symbol for display purposes
decimals = 0; // Amount of decimals for display purposes
}
function setInitialOwner(address to, uint tokenIndex) {
if (msg.sender != owner) revert();
if (allTokensAssigned) revert();
if (tokenIndex >= 10000) revert();
if (tokenIndexToAddress[tokenIndex] != to) {
if (tokenIndexToAddress[tokenIndex] != 0x0) {
balanceOf[tokenIndexToAddress[tokenIndex]]--;
} else {
tokensRemainingToAssign--;
}
tokenIndexToAddress[tokenIndex] = to;
balanceOf[to]++;
Assign(to, tokenIndex);
}
}
function setInitialOwners(address[] addresses, uint[] indices) {
if (msg.sender != owner) revert();
uint n = addresses.length;
for (uint i = 0; i < n; i++) {
setInitialOwner(addresses[i], indices[i]);
}
}
function allInitialOwnersAssigned() {
if (msg.sender != owner) revert();
allTokensAssigned = true;
}
function getToken(uint tokenIndex) {
if (!allTokensAssigned) revert();
if (tokensRemainingToAssign == 0) revert();
if (tokenIndexToAddress[tokenIndex] != 0x0) revert();
if (tokenIndex >= 10000) revert();
tokenIndexToAddress[tokenIndex] = msg.sender;
balanceOf[msg.sender]++;
tokensRemainingToAssign--;
Assign(msg.sender, tokenIndex);
}
// Transfer ownership of a token to another user without requiring payment
function transferToken(address to, uint tokenIndex) payable {
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] != msg.sender) revert();
if (tokenIndex >= 10000) revert();
if (tokensOfferedForSale[tokenIndex].isForSale) {
tokenNoLongerForSale(tokenIndex);
}
tokenIndexToAddress[tokenIndex] = to;
balanceOf[msg.sender]--;
balanceOf[to]++;
Transfer(msg.sender, to, 1);
TokenTransfer(msg.sender, to, tokenIndex);
// Check for the case where there is a bid from the new owner and refund it.
// Any other bid can stay in place.
Bid bid = tokenBids[tokenIndex];
if (bid.bidder == to) {
// Kill bid and refund value
pendingWithdrawals[to] += bid.value;
tokenBids[tokenIndex] = Bid(false, tokenIndex, 0x0, 0);
}
}
function tokenNoLongerForSale(uint tokenIndex) {
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] != msg.sender) revert();
if (tokenIndex >= 10000) revert();
tokensOfferedForSale[tokenIndex] = Offer(false, tokenIndex, msg.sender, 0, 0x0);
TokenNoLongerForSale(tokenIndex);
}
function offerTokenForSale(uint tokenIndex, uint minSalePriceInWei) {
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] != msg.sender) revert();
if (tokenIndex >= 10000) revert();
tokensOfferedForSale[tokenIndex] = Offer(true, tokenIndex, msg.sender, minSalePriceInWei, 0x0);
TokenOffered(tokenIndex, minSalePriceInWei, 0x0);
}
function offerTokenForSaleToAddress(uint tokenIndex, uint minSalePriceInWei, address toAddress) {
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] != msg.sender) revert();
if (tokenIndex >= 10000) revert();
tokensOfferedForSale[tokenIndex] = Offer(true, tokenIndex, msg.sender, minSalePriceInWei, toAddress);
TokenOffered(tokenIndex, minSalePriceInWei, toAddress);
}
function buyToken(uint tokenIndex) payable {
if (!allTokensAssigned) revert();
Offer offer = tokensOfferedForSale[tokenIndex];
if (tokenIndex >= 10000) revert();
if (!offer.isForSale) revert(); // token not actually for sale
if (offer.onlySellTo != 0x0 && offer.onlySellTo != msg.sender) revert(); // token not supposed to be sold to this user
if (msg.value < offer.minValue) revert(); // Didn't send enough ETH
if (offer.seller != tokenIndexToAddress[tokenIndex]) revert(); // Seller no longer owner of token
address seller = offer.seller;
tokenIndexToAddress[tokenIndex] = msg.sender;
balanceOf[seller]--;
balanceOf[msg.sender]++;
Transfer(seller, msg.sender, 1);
tokenNoLongerForSale(tokenIndex);
pendingWithdrawals[seller] += msg.value;
TokenBought(tokenIndex, msg.value, seller, msg.sender);
// Check for the case where there is a bid from the new owner and refund it.
// Any other bid can stay in place.
Bid bid = tokenBids[tokenIndex];
if (bid.bidder == msg.sender) {
// Kill bid and refund value
pendingWithdrawals[msg.sender] += bid.value;
tokenBids[tokenIndex] = Bid(false, tokenIndex, 0x0, 0);
}
}
function withdraw() payable {
if (!allTokensAssigned) revert();
uint amount = pendingWithdrawals[msg.sender];
// Remember to zero the pending refund before
// sending to prevent re-entrancy attacks
pendingWithdrawals[msg.sender] = 0;
msg.sender.transfer(amount);
}
function enterBidForToken(uint tokenIndex) payable {
if (tokenIndex >= 10000) revert();
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] == 0x0) revert();
if (tokenIndexToAddress[tokenIndex] == msg.sender) revert();
if (msg.value == 0) revert();
Bid existing = tokenBids[tokenIndex];
if (msg.value <= existing.value) revert();
if (existing.value > 0) {
// Refund the failing bid
pendingWithdrawals[existing.bidder] += existing.value;
}
tokenBids[tokenIndex] = Bid(true, tokenIndex, msg.sender, msg.value);
TokenBidEntered(tokenIndex, msg.value, msg.sender);
}
function acceptBidForToken(uint tokenIndex, uint minPrice) {
if (tokenIndex >= 10000) revert();
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] != msg.sender) revert();
address seller = msg.sender;
Bid bid = tokenBids[tokenIndex];
if (bid.value == 0) revert();
if (bid.value < minPrice) revert();
tokenIndexToAddress[tokenIndex] = bid.bidder;
balanceOf[seller]--;
balanceOf[bid.bidder]++;
Transfer(seller, bid.bidder, 1);
tokensOfferedForSale[tokenIndex] = Offer(false, tokenIndex, bid.bidder, 0, 0x0);
uint amount = bid.value;
tokenBids[tokenIndex] = Bid(false, tokenIndex, 0x0, 0);
pendingWithdrawals[seller] += amount;
TokenBought(tokenIndex, bid.value, seller, bid.bidder);
}
function withdrawBidForToken(uint tokenIndex) {
if (tokenIndex >= 10000) revert();
if (!allTokensAssigned) revert();
if (tokenIndexToAddress[tokenIndex] == 0x0) revert();
if (tokenIndexToAddress[tokenIndex] == msg.sender) revert();
Bid bid = tokenBids[tokenIndex];
if (bid.bidder != msg.sender) revert();
TokenBidWithdrawn(tokenIndex, bid.value, msg.sender);
uint amount = bid.value;
tokenBids[tokenIndex] = Bid(false, tokenIndex, 0x0, 0);
// Refund the bid money
msg.sender.transfer(amount);
}
}
2) 创建后,我想从 Oraclize 获取一些数据,并根据外汇美元/英镑汇率转移代币或不转移代币。 以下代码来自 Oraclize 示例合约:
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract ExampleContract is usingOraclize {
string public EURGBP;
string public value = "0.88086";
event LogPriceUpdated(string price);
event LogNewOraclizeQuery(string description);
function ExampleContract() payable public{
updatePrice();
}
function __callback(bytes32 myid, string result) public {
if (msg.sender != oraclize_cbAddress()) revert();
EURGBP = result;
if (keccak256(result) != keccak256(value)) {
LogPriceUpdated(value);
}
else {
LogPriceUpdated(result);
}
}
function updatePrice() payable public{
if (oraclize_getPrice("URL") > this.balance) {
LogNewOraclizeQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
} else {
LogNewOraclizeQuery("Oraclize query was sent, standing by for the answer..");
oraclize_query("URL", "json(http://api.fixer.io/latest?symbols=USD,GBP).rates.GBP");
}
}
}
根据我的理解,我可以让主代币合约继承自预言机合约。并且主合约应该继承预言机代币合约的所有功能。
Oraclize 是一项付费服务,所以我应该让 updatePrice() 始终支付,并在 Remix IDE 的右上方放置 1 ether 之类的东西。
问题是双重的:
a) 在官方 Remix IDE (JS VM) 中,当代币合约执行时,Oraclize 合约失败并显示“将合约恢复到初始状态”消息。是否与支付甲骨文有关?因为我总是在 IDE 的右上角放 1 个以太币。但我不知道如何准确解决这个问题。
b)在 Oraclize 拥有的 Remix 分支(https://dapps.oraclize.it/browser-solidity/)中也使用 JS VM,它将执行查询,但执行令牌失败,并显示“无效的操作码”消息对于“电话”。所以我什至无法获得令牌符号。
问题:
1)此外,除了 IDE 问题之外,我的疑问还在于,在美元/英镑值为 X 的情况下,我应该如何继续提供代币。
我假设我应该在主合约中使用 getToken() 函数,检查汇率是否为 x,并分配代币?我怎样才能有效地做到这一点?
2)我应该使用主代币合约中实现的事件之一,还是与它无关?