我正在尝试构建一个简单的票务应用程序,买家可以在其中从卖家那里购买门票(即 NFT)。注意:我在编码方面没有太多经验,但开始很好地理解 Solidity 中的概念。
对我来说,如果卖方创建一份包含最大门票、价格等的合同,如果转移了正确的金额,买方就可以从中铸造(与先铸造然后出售相比,节省了汽油费),这是最有意义的。请参阅下面的代码。正如您所注意到的,只有部署者才能更改参数以列出具有不同参数的多个事件(即场合)。
我的问题与合约的部署有关,因为我希望不同的卖家能够通过一个简单的按钮和一些输入从前端部署合约,之后买家能够从这些合约中铸造门票/NFT。这可能吗?如果可以,如何实现?如果有人有更好的解决方案,也很好! :)
干杯,
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
contract TicketBlock is ERC721 {
address public owner;
uint public numOccasions;
uint public totalSupply;
struct Occasion {
uint id;
string name;
uint cost;
uint tickets;
uint maxTickets;
}
mapping(uint => Occasion) occasions;
mapping(uint => mapping(address => bool)) public hasBought;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
constructor(string memory _name, string memory _symbol) ERC721 (_name, _symbol) {
owner = msg.sender;
}
function list(string memory _name, uint _cost, uint _maxTickets) public onlyOwner {
numOccasions++;
occasions[numOccasions] = Occasion(
numOccasions,
_name,
_cost,
_maxTickets,
_maxTickets
);
}
function mint (uint _id, uint _seat) public payable {
require(_id != 0);
require(_id <= numOccasions);
require(msg.value >= occasions[_id].cost);
occasions[_id].tickets -= 1; // <-- Update ticket account
hasBought[_id][msg.sender] = true; // <-- Update buying status
totalSupply++;
_safeMint(msg.sender,totalSupply);
}
}
你必须实现工厂合约逻辑:
工厂合约是生产其他智能产品的智能合约 合同。就像鞋厂按照一定的标准生产鞋子一样 标准,工厂合同将确保所有的智能 它产生的合同遵守某些任意的品质。这 这是您看到许多(如果不是全部)大型 dApp 使用的常见模式。 例如,如果您熟悉 Uniswap,他们会实现这个 图案。每次您与 Uniswap 池交互时,该池 实际上是 Uniswap 工厂生成的智能合约 合同。
工厂合同的一个例子是:
contract TicketFactory {
address[] public deployedTickets;
uint public ticketsCount;
// args will be the args that Ticket conract's constructor need.
// person who calls this should be marked as manager
function createTicket(string memory _name, string memory _symbol) public {
// msg.sender is the user who tries to create the Ticket
Ticket newTicketContract = new Ticket(string memory _name, string memory _symbol);
deployedTickets.push(address(newTicketContract));
ticketsCount++;
}
function getDeployedTicket(uint index) public view returns (address) {
return deployedTickets[index];
}
function getTicketCounts() public view returns (uint) {
return ticketsCount;
}
// you can add more logic
}
在
createTicket
函数中,您会看到 new
关键字。我在这里解释了使用和不使用 `new` 关键字创建新的 Solidity 合约有什么区别?
使用 new 关键字创建新合约会部署并创建一个新合约 合约实例。部署合约涉及检查是否 发送者已提供足够的gas来完成部署。
要将其与前端集成,您将与 TicketFactory 合约进行交互,导入已部署的工厂合约
import factoryTicket from "../ethereumContracts/campaignFactoryInstance";
您应该有一个表单来创建票证合约,因此在用户输入
createTicket
函数参数的输入后,您可以提交该表单。所以你应该有一个 onSubmit
方法和内部
const onSubmit = async (event) => {
event.preventDefault();
if (window.ethereum) {
const connected_account = await window.ethereum.request({
method: "eth_requestAccounts",
});
try {
setLoading(true);
setError("");
await factory.methods
.createTicket(nameState,symbolState)
// whenever you send transactions in the browser, metamask will automatically calculate the amount of gas we need to run this function. so we dont need to specify
.send({
from: connected_account[0],
});
setSuccessMessage("New Ticket successfully created");
Router.push("/");
} catch (err) {
setError(err.message);
}
setLoading(false);
}
};
我更喜欢一种更简单的方法,部署一个合约,任何卖家都可以列出一个活动,任何买家都可以为所选活动制作一张门票。这将为您节省为每个卖家部署单独合同的汽油费。
实现这一点的方法很简单,只需在场合结构中添加一个“seller”字段即可:
struct Occasion {
uint id;
string name;
uint cost;
uint tickets;
uint maxTickets;
address seller;
}
从列表功能中删除onlyOwner并添加代码以自动将卖家设置为
msg.sender
对于铸币功能,您可以添加一行将事件成本转移给卖家,这样您就可以开始了:
function mint (uint _id, uint _seat) public payable {
require(_id != 0);
require(_id <= numOccasions);
require(msg.value >= occasions[_id].cost);
address payable receiver = payable(occasions[_id].seller);
receiver.transfer(msg.value);
occasions[_id].tickets -= 1; // <-- Update ticket account
hasBought[_id][msg.sender] = true; // <-- Update buying status
totalSupply++;
_safeMint(msg.sender,totalSupply);
}
如果您将此作为服务提供,您可以设置一笔费用转给合同所有者。