1. 元宇宙核心技術(shù)
騰訊最近發(fā)布了一個(gè)全真互聯(lián)白皮書,雖然他們強(qiáng)調(diào)全真互聯(lián)
跟元宇宙
不同,但怎么看都像是無(wú)奈之下的牽強(qiáng)附會(huì)。從核心技術(shù)上來(lái)看,其實(shí)元宇宙
、Web3.0
和這個(gè)全真互聯(lián)
都是一回事兒,都是前端和后端兩方面技術(shù)發(fā)展的產(chǎn)物:
- 隨著前端交互技術(shù)(既包括
軟件渲染技術(shù)
,也包括硬件交互設(shè)備
)的發(fā)展,互聯(lián)網(wǎng)從只能在PC上看PGC的Web1.0,發(fā)展到還可以在手機(jī)上看UGC的Web2.0。到了今天,發(fā)展出了又能在各種智能穿戴設(shè)備上,用不同的人類感官,去交互三維內(nèi)容的Web3.0,也就是元宇宙。 - 內(nèi)容的種類和規(guī)模的增加,必然要求后端計(jì)算、存儲(chǔ)、網(wǎng)絡(luò)的處理能力的增強(qiáng)。
云計(jì)算全棧軟硬件技術(shù)
的不斷演進(jìn)支持了Web1.0、2.0到3.0的變化,只是到了3.0,也就是元宇宙,后端又增加了另一項(xiàng)配合社會(huì)形態(tài)轉(zhuǎn)變的智能合約
技術(shù)(比如NFT-非同質(zhì)數(shù)字化資產(chǎn)、數(shù)字貨幣等),智能合約當(dāng)然不見得要跟區(qū)塊鏈連在一起,但區(qū)塊鏈確實(shí)是實(shí)現(xiàn)它的一種有效方式。
騰訊的全真互聯(lián)確實(shí)刻意的回避了智能合約,這也不奇怪,乖巧如騰訊者深諳個(gè)中道理,不可能碰這種危險(xiǎn)的東西,這也是為啥國(guó)內(nèi)元宇宙初創(chuàng)公司大多數(shù)都集中在前端技術(shù)的原因,而且騰訊自己是Web2.0的既得利益者,商業(yè)上,顯然也不愿意去掉包括自己在內(nèi)的這些中心互聯(lián)網(wǎng)平臺(tái)。
為了從技術(shù)上實(shí)操完整的元宇宙應(yīng)用開發(fā),本文選擇Decentraland作為做實(shí)驗(yàn)的平臺(tái)。
2. 元宇宙實(shí)例及應(yīng)用實(shí)例
關(guān)于本實(shí)例完整的業(yè)務(wù)模式描述,請(qǐng)參見前面的文章。本文是從技術(shù)角度記錄下驗(yàn)證的實(shí)際開發(fā)過程。
Decentraland是一個(gè)基于以太坊區(qū)塊鏈實(shí)現(xiàn)的分布式虛擬現(xiàn)實(shí)平臺(tái)。它是一個(gè)高度符合元宇宙定義的虛擬世界,這個(gè)世界的成員可以在它的土地
上創(chuàng)建內(nèi)容
和應(yīng)用
,體驗(yàn)
他人的內(nèi)容和應(yīng)用,用自己的內(nèi)容和應(yīng)用賺錢
。Decentraland中的土地是用以太坊智能合約維護(hù)的一種NFT,里面的空間是3D的,用戶可以在里面游逛,土地總數(shù)有限。土地被分為Parcel
,Parcel用坐標(biāo)來(lái)標(biāo)識(shí),這些Parcel被這個(gè)世界的成員們永久持有,可以用MANA(馬那幣)交易,MANA是Decentraland的官方加密數(shù)字貨幣。用戶可以全權(quán)控制他們的土地,在上面用現(xiàn)成或自定義的3D前端組件,創(chuàng)建靜態(tài)3D場(chǎng)景或可交互的游戲和應(yīng)用。
本文實(shí)現(xiàn)的就是一個(gè)自定義的3D可交互前端組件(在Decentraland叫做Smart Item
),組件里包含由一個(gè)以太坊智能合約維護(hù)的、可交互的廣告內(nèi)容。這個(gè)合約是個(gè)4方合約,參與方有組件運(yùn)營(yíng)者
、土地所有者
、廣告發(fā)布者
、消費(fèi)者
。整個(gè)合約發(fā)生作用的過程如下:
- 組件運(yùn)營(yíng)者把組件發(fā)布到
Decentraland Builder
里 - 廣告發(fā)布者將廣告發(fā)布到組件上
- 土地所有者在Decentraland Builder里把組件裝配在自己土地上的建筑上
- 消費(fèi)者來(lái)這個(gè)建筑里,點(diǎn)擊廣告,廣告發(fā)布者按約定的金額,支付MANA給土地所有者和組件運(yùn)營(yíng)者
- 消費(fèi)者打開廣告后,發(fā)生了購(gòu)買行為,廣告發(fā)布者按約定的金額,支付MANA給消費(fèi)者
3. 以太坊里的智能合約開發(fā)
3.1. World Wide Web的訪問能力
首先,使用代理或其他方式,獲得World Wide網(wǎng)絡(luò)訪問能力。
3.2. 初始化以太坊錢包
- 在
chrome應(yīng)用商店
中安裝chrome插件形式
的加密貨幣錢包MetaMask,并依提示創(chuàng)建默認(rèn)賬戶。 - 在chrome右上角的
MetaMask插件界面
中,將默認(rèn)賬戶的名字改為“組件運(yùn)營(yíng)者”。 - 繼續(xù)創(chuàng)建三個(gè)賬戶,分別命名為“土地所有者”、“廣告發(fā)布者”、“消費(fèi)者”。
- 將當(dāng)前所在鏈從
以太坊Ethereum主網(wǎng)絡(luò)
,切換至Ropsten測(cè)試網(wǎng)絡(luò)
。注意:這個(gè)測(cè)試網(wǎng)絡(luò)只能用到22年Q4末,隨著以太坊完成了“Merge”,此測(cè)試網(wǎng)絡(luò)將下線,測(cè)試需要切換至新網(wǎng)絡(luò),建議使用Sepolia test network
,不過,現(xiàn)在Decentraland還不支持這個(gè)新測(cè)試網(wǎng)絡(luò)。 - 打開Ropsten測(cè)試網(wǎng)絡(luò)的代幣免費(fèi)獲取頁(yè)面,將賬戶依次切換至“組件運(yùn)營(yíng)者”、“廣告發(fā)布者”、“消費(fèi)者”,為這三個(gè)賬戶分別充值1個(gè)
以太幣
,轉(zhuǎn)賬需要在MetaMask面板上點(diǎn)擊確定。這些以太幣是用于支付在以太坊上執(zhí)行交易所需支付的成本。如果使用“Sepolia test network”,則需要使用Sepolia的代幣免費(fèi)獲取頁(yè)面,不過它的代幣到賬時(shí)間需要大概2個(gè)小時(shí)。 - 打開Decentraland在Ropster上的MANA測(cè)試代幣免費(fèi)獲取頁(yè)面,為“廣告發(fā)布者”賬戶充值MANA代幣,轉(zhuǎn)賬需要在MetaMask面板上點(diǎn)擊確定。這些MANA是4方合約在執(zhí)行交易時(shí)使用的貨幣。
3.3. 開發(fā)4方合約
以太坊智能合約
開發(fā)使用的語(yǔ)言是Solidity,使用Solidity讓人想起用c++寫系統(tǒng)軟件,對(duì)內(nèi)存精細(xì)規(guī)劃,對(duì)代碼極致雕琢。這是因?yàn)楹霞s要被存儲(chǔ)到鏈上并在鏈上執(zhí)行,代碼體積和執(zhí)行內(nèi)存越大,消耗的資源越多,要付的錢就越多。它必須很干凈,能刪除掉的都要?jiǎng)h掉,注釋不要有,甚至變量名都應(yīng)該越短越好。本文中代碼注釋是為了說(shuō)明邏輯,實(shí)際生產(chǎn)運(yùn)行時(shí)一定要?jiǎng)h掉注釋,縮短變量名。
合約的開發(fā)工具為Remix。Remix可進(jìn)行本地部署,但驗(yàn)證過程就直接使用SaaS版本了。打開Remix,越過歡迎界面,進(jìn)入開發(fā)界面,在左側(cè)選擇File explorer
,然后在contracts
文件夾下,創(chuàng)建一個(gè)新的Solidity程序文件,命名為dmall.sol
(此實(shí)例中的合約名字定為DMall
),并將以下代碼復(fù)制進(jìn)文件中:
//SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.7.0 <0.9.0;
// 聲明MANAToken的接口,用于DMall合約中調(diào)用
interface MANAToken {
// 廣告所有者必須在發(fā)布廣告之前,調(diào)用MANAToken的這個(gè)接口,允許DMall從他的MANA賬戶里劃轉(zhuǎn)代幣到其他賬戶,其中_sender參數(shù)為DMall部署后的地址,_value參數(shù)為允許劃轉(zhuǎn)的MANA代幣數(shù)量的上限
function approve(address _sender, uint256 _value) external returns (bool);
// DMall合約會(huì)調(diào)用這個(gè)接口,將MANA代幣從廣告所有者轉(zhuǎn)賬到其他三個(gè)賬戶,_from是廣告所有者,_to是其他三個(gè)賬戶,_value是轉(zhuǎn)賬金額
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
}
contract DMall {
// 廣告的狀態(tài)枚舉,廣告所有者首次調(diào)用uptAd接口創(chuàng)建接口后為Created,組件運(yùn)營(yíng)者調(diào)用apprAd接口同意后變?yōu)锳pproved,廣告所有者再次調(diào)用uptAd接口后為Updated,此時(shí)還需要組件運(yùn)營(yíng)者調(diào)用apprAd接口。只有Approved狀態(tài)的廣告可以被看到和購(gòu)買。Undefined是初始狀態(tài),無(wú)意義,Invalid是廣告因?yàn)槠渌颍ㄈ鐝V告發(fā)布者沒給MANA)失效。
enum AdState { Undefined, Created, Approved, Updated, Invalid }
// 廣告點(diǎn)擊記錄的狀態(tài),Undefined是初始狀態(tài),無(wú)意義,Clicked表示消費(fèi)者點(diǎn)擊過了(即調(diào)用clickAd),Bought表示消費(fèi)者買過了(即調(diào)用buyAd)。
enum ClickState { Undefined, Clicked, Bought }
// 在鏈上存儲(chǔ)的一條廣告的數(shù)據(jù)結(jié)構(gòu)
struct Ad {
// 廣告點(diǎn)擊時(shí),廣告發(fā)布者給土地所有者的MANA數(shù)量
uint m2L;
// 廣告點(diǎn)擊時(shí),廣告發(fā)布者給組件運(yùn)營(yíng)者的MANA數(shù)量
uint m2O;
// 廣告被購(gòu)買時(shí),廣告發(fā)布者給消費(fèi)者的MANA數(shù)量
uint m2C;
// 廣告的狀態(tài)
AdState state;
// 廣告的點(diǎn)擊記錄數(shù)據(jù)集合,第一個(gè)地址是土地所有者的地址,第二個(gè)地址是消費(fèi)者的地址,最終里面存的數(shù)據(jù)是一條點(diǎn)擊數(shù)據(jù)的狀態(tài)
mapping(address => mapping(address => ClickState)) clicks;
}
// MANAToken合約的地址
MANAToken manaToken;
// 廣告數(shù)據(jù)集合,第一個(gè)地址是廣告發(fā)布者的地址,第二個(gè)整數(shù)是廣告ID,數(shù)據(jù)是上面的Ad結(jié)構(gòu)體
mapping(address => mapping(uint => Ad)) public ads;
// 合約運(yùn)營(yíng)者,就是組件運(yùn)營(yíng)者的地址
address public op;
// 合約構(gòu)造函數(shù),用調(diào)用者的地址初始化合約運(yùn)營(yíng)者地址,用輸入的MANAToken合約地址初始化對(duì)應(yīng)數(shù)據(jù),由所在的鏈?zhǔn)巧a(chǎn)鏈還是測(cè)試鏈決定
constructor(address _manaToken) {
op = msg.sender;
manaToken = MANAToken(_manaToken);
}
// 因?yàn)殒溕系牟僮鞫际钱惒降?,需要存在鏈上才算成功,以下事件是?duì)應(yīng)接口調(diào)用成功后的回調(diào),可在前端應(yīng)用程序中實(shí)現(xiàn)
// 廣告創(chuàng)建成功后的事件(uptAd接口首次調(diào)用后的事件)
event AdCrted(address merchant, uint adId, uint m2L, uint m2O, uint m2C);
// 廣告更新成功后的事件(uptAd接口再次調(diào)用后的事件)
event AdUpted(address merchant, uint adId, uint oM2L, uint oM2O, uint oM2C, uint m2L, uint m2O, uint m2C);
// 廣告批準(zhǔn)成功后的事件(apprAd接口調(diào)用后的事件)
event AdAppred(address merchant, uint adId);
// 廣告點(diǎn)擊后的事件(clickAd接口調(diào)用后的事件)
event AdClicked(address merchant, uint adId, address landowner, address consumer);
// 廣告失效事件(clickAd接口或buyAd接口中發(fā)生失敗后的事件)
event AdInvalid(address merchant, uint adId);
// 廣告被購(gòu)買事件(buyAd接口調(diào)用后的事件)
event AdBought(address merchant, uint adId, address landowner, address consumer);
// 創(chuàng)建或更新廣告,由廣告發(fā)布者調(diào)用,_adId是廣告的ID,_m2L是廣告發(fā)布者愿意給土地所有者的MANA數(shù)量,_m2O是廣告發(fā)布者愿意給組件運(yùn)營(yíng)者的MANA數(shù)量,_m2C是廣告發(fā)布者愿意給消費(fèi)者的MANA數(shù)量
function uptAd(uint _adId, uint _m2L, uint _m2O, uint _m2C) public {
// 初始化一個(gè)Ad對(duì)象
Ad storage ad = ads[msg.sender][_adId];
if (ad.state == AdState.Undefined) {
// 如果這是個(gè)新廣告,則設(shè)置參數(shù),并觸發(fā)AdCrted
ad.state = AdState.Created;
ad.m2L = _m2L;
ad.m2O = _m2O;
ad.m2C = _m2C;
emit AdCrted(msg.sender, _adId, _m2L, _m2O, _m2C);
}
else {
// 如果不是新廣告,則記錄當(dāng)前參數(shù),設(shè)置新參數(shù),并觸發(fā)AdUpted
ad.state = AdState.Updated;
uint oM2L = ad.m2L;
uint oM2O = ad.m2O;
uint oM2C = ad.m2C;
ad.m2L = _m2L;
ad.m2O = _m2O;
ad.m2C = _m2C;
emit AdUpted(msg.sender, _adId, oM2L, oM2O, oM2C, _m2L, _m2O, _m2C);
}
}
// 批準(zhǔn)廣告,由組件運(yùn)營(yíng)者調(diào)用, _merchant是廣告發(fā)布者地址,_adId是廣告的ID
function apprAd(address _merchant, uint _adId) public {
// 要求只能是組件所有者調(diào)用
require(msg.sender == op, "Only DMall operator can approve the deployment of Ad");
// 要求廣告不是初始狀態(tài)和批準(zhǔn)過的狀態(tài)
require(ads[_merchant][_adId].state != AdState.Undefined && ads[_merchant][_adId].state != AdState.Approved , "The ad of merchant with the ID does not exist or has been approved");
// 設(shè)置廣告狀態(tài)
ads[_merchant][_adId].state = AdState.Approved;
// 觸發(fā)AdAppred事件
emit AdAppred(_merchant, _adId);
}
// 點(diǎn)擊廣告,由消費(fèi)者調(diào)用,_merchant是廣告發(fā)布者地址,_adId是廣告的ID,_landowner是廣告所在土地的土地所有者
function clickAd(address _merchant, uint _adId, address _landowner) public {
// 需要廣告是Approved的
require(ads[_merchant][_adId].state == AdState.Approved, "The ad of merchant with the ID has not been approved");
// 需要廣告不是已被此消費(fèi)者點(diǎn)擊狀態(tài)
require(ads[_merchant][_adId].clicks[_landowner][msg.sender] != ClickState.Clicked, "The clicking had been paid but does not lead to buying yet");
// 廣告發(fā)布者按約定給土地所有者和組件運(yùn)營(yíng)者轉(zhuǎn)賬
if (manaToken.transferFrom(_merchant, _landowner, ads[_merchant][_adId].m2L) && manaToken.transferFrom(_merchant, op, ads[_merchant][_adId].m2O) ) {
// 如果轉(zhuǎn)賬成功,則標(biāo)記點(diǎn)擊狀態(tài),觸發(fā)AdClicked
ads[_merchant][_adId].clicks[_landowner][msg.sender] = ClickState.Clicked;
emit AdClicked(_merchant, _adId, _landowner, msg.sender);
}
else {
// 轉(zhuǎn)賬失敗,則標(biāo)記廣告失效,觸發(fā)AdInvalid
ads[_merchant][_adId].state = AdState.Invalid;
emit AdInvalid(_merchant, _adId);
}
}
// 購(gòu)買廣告商品,由消費(fèi)者調(diào)用,_merchant是廣告發(fā)布者地址,_adId是廣告的ID,_landowner是廣告所在土地的土地所有者
function buyAd(address _merchant, uint _adId, address _landowner) public {
// 需要消費(fèi)者點(diǎn)擊過此廣告
require(ads[_merchant][_adId].clicks[_landowner][msg.sender] == ClickState.Clicked, "The consumer does not click the ad or has bought");
// 廣告發(fā)布者轉(zhuǎn)賬給消費(fèi)者
if (manaToken.transferFrom(_merchant, msg.sender, ads[_merchant][_adId].m2C)) {
// 轉(zhuǎn)賬成功,則將廣告點(diǎn)擊記錄標(biāo)記為Bought,并觸發(fā)AdBought
ads[_merchant][_adId].clicks[_landowner][msg.sender] = ClickState.Bought;
emit AdBought(_merchant, _adId, _landowner, msg.sender);
}
else {
// 轉(zhuǎn)賬失敗,則標(biāo)記廣告失效,觸發(fā)AdInvalid
ads[_merchant][_adId].state = AdState.Invalid;
emit AdInvalid(_merchant, _adId);
}
}
}
3.4. 手動(dòng)運(yùn)行DMall智能合約
- 在chrome右上角MetaMask插件的面板里,確保切換到“Ropsten測(cè)試網(wǎng)絡(luò)”鏈和“組件運(yùn)營(yíng)者”賬號(hào)
- 在Remix左側(cè),選擇
Solidity Compiler
,點(diǎn)擊Compile dmall.sol
按鈕 -
ENEIRONMENT
選擇Injected Provide - Metamask
,CONTRACT
選擇DMall
- 在Decentraland中的智能合約地址頁(yè)面,找到Ropsten網(wǎng)絡(luò)的MANAToken地址,即
0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb
- 將上述地址填寫進(jìn)
Deploy
按鈕后的輸入框,然后點(diǎn)擊Deploy按鈕,等待MetaMask面板上彈出確認(rèn)
并點(diǎn)擊,完成DMall合約部署 - 重選選擇CONTRACT為
MANAToken
,將前面的MANAToken地址填入At Address
按鈕后的輸入框,然后點(diǎn)擊At Address按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成MANAToken合約在Remix上的顯示,這個(gè)不是我們部署的,只是為了能調(diào)用接口 - 在MetaMask面板上,切換至廣告發(fā)布者賬號(hào)
- 在
Deployed Contracts
下,找到并復(fù)制DMALL
合約的地址,然后打開MANATOKEN
合約,打開approve
接口,輸入_sender
為上面復(fù)制的DMALL地址,_value
輸入個(gè)大數(shù),如100000。點(diǎn)擊transact
按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成廣告發(fā)布者對(duì)DMALL合約的授權(quán) - 打開DMALL合約的
uptAd
接口,輸入_adId
=1,_m2L
=11,_m2O
=12,_m2C
=13,點(diǎn)擊transact
按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成廣告創(chuàng)建 - 在MetaMask面板上,復(fù)制廣告發(fā)布者的地址,然后切換至組件運(yùn)營(yíng)者賬號(hào)
- 打開DMALL合約的
apprAd
接口,輸入_merchant
為剛才復(fù)制的地址,_adId
為1,點(diǎn)擊transact
按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成廣告批準(zhǔn) - 在MetaMask面板上,切換至消費(fèi)者賬號(hào)
- 打開DMALL合約的
clickAd
接口,輸入_merchant
為廣告發(fā)布者地址,_adId
為1,_landowner
為土地所有者的地址,點(diǎn)擊transact
按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成廣告點(diǎn)擊 - 此時(shí),可以通過MetaMask面板上的賬戶詳情頁(yè)面,查看以太幣和MANA幣的各賬戶變化情況
- 打開DMALL合約的
buyAd
接口,輸入_merchant
為廣告發(fā)布者地址,_adId
為1,_landowner
為土地所有者的地址,點(diǎn)擊transact
按鈕,等待MetaMask面板上彈出確認(rèn)并點(diǎn)擊,完成廣告購(gòu)買 - 此時(shí),可以再通過MetaMask面板上的賬戶詳情頁(yè)面,查看以太幣和MANA幣的各賬戶變化情況
3.5. Python調(diào)用DMall智能合約
- 創(chuàng)建可供代碼調(diào)用的以太坊API接口地址
- 在infura頁(yè)面上,注冊(cè)用戶并登錄,選擇右上角的
Dashboard
進(jìn)入 - 選擇右上角的
CREATE NEW KEY
按鈕 - 網(wǎng)絡(luò)選擇
Web3 API
- 在
Ethereum
下,從MAINNET
切換到Ropsten
,不過現(xiàn)在Ropsten,已經(jīng)消失了,大家可以按照以下格式,將key替換進(jìn)去即可,或者直接用也行:https://ropsten.infura.io/v3/dd4cc999659f448d905400a4e8fb4e9d
- 在infura頁(yè)面上,注冊(cè)用戶并登錄,選擇右上角的
- 安裝PyCharm
- 創(chuàng)建合約接口文件
- 在PyCharm中創(chuàng)建一個(gè)工作區(qū),在里面增加一個(gè)
contract_abi.py
文件 - 在Remix左側(cè),選擇
Solidity Compiler
,CONTRACT選擇DMALL,點(diǎn)擊下方的ABI復(fù)制按鈕
- 在contract_abi.py中,輸入
dmall_abi = """"""
,在六個(gè)分號(hào)的正中間粘貼上面的復(fù)制內(nèi)容 - 在MANAToken的代碼頁(yè)面,點(diǎn)擊
Contract ABI
部分右上角的Copy ABI to clipboard
- 在contract_abi.py中,換行輸入
manatoken_abi = """"""
,在六個(gè)分號(hào)的正中間粘貼上面的復(fù)制內(nèi)容
- 在PyCharm中創(chuàng)建一個(gè)工作區(qū),在里面增加一個(gè)
- 在MetaMask面板中,導(dǎo)出廣告發(fā)布者的Private Key
- 在PyCharm中,為項(xiàng)目安裝Web3包(pip install web3),創(chuàng)建dmalltest.py,粘貼以下代碼,按注釋調(diào)試驗(yàn)證合約的部分接口
import time
from web3 import Web3, HTTPProvider
from web3.logs import IGNORE
import contract_abi
# DMall合約的地址,從Remix中獲得,輸入單引號(hào)之間
dmall_address = Web3.toChecksumAddress('')
# MANAToken合約的地址
manatoken_address = Web3.toChecksumAddress('0x2a8fd99c19271f4f04b1b7b9c4f7cf264b626edb')
# 廣告發(fā)布者的Private Key,將前文導(dǎo)出的值輸入單引號(hào)之間
merchant_private_key = ''
# 廣告發(fā)布者的賬號(hào)地址,從MetaMask面板中獲得,輸入單引號(hào)之間
merchant_address = ''
# 土地所有者的賬號(hào)地址,從MetaMask面板中獲得,輸入單引號(hào)之間
landowner_address = ''
# 消費(fèi)者的賬號(hào)地址,從MetaMask面板中獲得,輸入單引號(hào)之間
consumer_address = ''
# 以太坊API地址,將前文infura獲取的https地址輸入單引號(hào)之間
w3 = Web3(HTTPProvider(''))
# 初始化DMall和MANAToken合約的接口和事件
dmall_contract = w3.eth.contract(address=dmall_address, abi=contract_abi.dmall_abi)
manatoken_contract = w3.eth.contract(address=manatoken_address, abi=contract_abi.manatoken_abi)
# 廣告所有者在MANAToken合約上批準(zhǔn)DMall合約對(duì)其賬戶的MANA進(jìn)行操作
def m_appr_dmall(dmall, amount):
nonce = w3.eth.getTransactionCount(merchant_address)
# 創(chuàng)建MANAToken上的一個(gè)交易,內(nèi)容就是執(zhí)行approve接口
txn_dict = manatoken_contract.functions.approve(dmall, amount).buildTransaction({
'chainId': 3,
'gas': 140000,
'gasPrice': w3.toWei('40', 'gwei'),
'nonce': nonce,
})
# 用廣告所有者的Private Key給上述交易簽名
signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=merchant_private_key)
# 執(zhí)行交易
result = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
# 輪詢等待交易成功
tx_receipt = None
count = 0
while tx_receipt is None and (count < 30):
time.sleep(10)
try:
tx_receipt = w3.eth.get_transaction_receipt(result)
print(tx_receipt)
except Exception as e:
print('error: ', e)
if tx_receipt is None:
return {'status': 'failed', 'error': 'timeout'}
# 處理異步返回的事件
processed_receipt = manatoken_contract.events.Approval().processReceipt(tx_receipt, errors=IGNORE)
print(processed_receipt)
return {'status': 'added', 'processed_receipt': processed_receipt}
# 廣告所有者發(fā)布廣告
def m_pub_ad(ad_id, m_2_l, m_2_o, m_2_c):
nonce = w3.eth.getTransactionCount(merchant_address)
# 創(chuàng)建DMall上的一個(gè)交易,內(nèi)容就是執(zhí)行創(chuàng)建廣告的接口
txn_dict = dmall_contract.functions.uptAd(ad_id, m_2_l, m_2_o, m_2_c).buildTransaction({
'chainId': 3,
'gas': 140000,
'gasPrice': w3.toWei('40', 'gwei'),
'nonce': nonce,
})
# 用廣告所有者的Private Key給上述交易簽名
signed_txn = w3.eth.account.signTransaction(txn_dict, private_key=merchant_private_key)
# 執(zhí)行交易
result = w3.eth.sendRawTransaction(signed_txn.rawTransaction)
# 輪詢等待交易成功
tx_receipt = None
count = 0
while tx_receipt is None and (count < 30):
time.sleep(10)
try:
tx_receipt = w3.eth.get_transaction_receipt(result)
print(tx_receipt)
except Exception as e:
print('error: ', e)
if tx_receipt is None:
return {'status': 'failed', 'error': 'timeout'}
# 處理異步返回的事件
processed_receipt = dmall_contract.events.AdCrted().processReceipt(tx_receipt)
print(processed_receipt)
return {'status': 'added', 'processed_receipt': processed_receipt}
if __name__ == "__main__":
# 可分別打開以下兩行注釋符號(hào),執(zhí)行對(duì)應(yīng)函數(shù),驗(yàn)證合約執(zhí)行情況
m_appr_dmall(dmall_address, 100)
#m_pub_ad(2, 21, 22, 23)
4. Decentraland里的Smart Item開發(fā)
Decentraland使用Builder(需使用以太坊Ethereum主網(wǎng)絡(luò)登錄)搭建Scene
,Scene被部署到實(shí)際的Land
中,就成為建筑物。搭建Scene的組件有普通和Smart Item兩種,Smart Item就是可以響應(yīng)用戶操作,執(zhí)行代碼的組件??蓮膅ithub上獲取現(xiàn)有Smart Item代碼,修改代碼,驗(yàn)證合約執(zhí)行:
git clone git@github.com:decentraland/smart-items.git
可以選擇一個(gè)現(xiàn)有的Smart Item,在其item.ts
源代碼的spawn
函數(shù)中,增加如下代碼,調(diào)用智能合約文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-462146.html
ent.addComponent(
new OnPointerDown(
async function () {
const provider = await getProvider()
const requestManager = new RequestManager(provider)
const factory = new ContractFactory(requestManager, abi)
// 需將從Remix中獲取的DMall合約地址,輸入雙引號(hào)之間
const contract = (await factory.at(
""
)) as any
const address = await getUserAccount()
log(address)
const res = await contract.clickAd(
// 需將從MetaMask中獲取的廣告發(fā)布者賬號(hào)地址,輸入雙引號(hào)之間
"",
0,
// 需將從MetaMask中獲取的土地所有者賬號(hào)地址,輸入雙引號(hào)之間
"",
{
from: address,
}
)
log(res)
// 打開外部的商品購(gòu)買鏈接
openExternalURL("https://item.jd.com/10045659650093.html")
},
{
button: ActionButton.PRIMARY,
hoverText: locationString,
}
)
)
接下來(lái)可按如下步驟上傳Smart Item到Builder進(jìn)行驗(yàn)證使用:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-462146.html
- 安裝Node.js
- 安裝Decentraland
npm install -g decentraland
- 到對(duì)應(yīng)的Smart Item源代碼文件夾下,在本地啟動(dòng)進(jìn)行交互驗(yàn)證
dcl install
dcl start
- 也可以打包上傳到Builder使用
dcl pack
到了這里,關(guān)于元宇宙應(yīng)用開發(fā)實(shí)例——以太坊里的智能合約和Decentraland里的3D前端交互組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!