基於以太坊和USDC搭建去中心化金融系統

買賣虛擬貨幣
在Coinbase,我們希望可以建立一個開放的金融系統。我們堅信提高金融的自由度可以讓世界更美好。去中心化金融,簡稱DeFi是一個開放,無界限並且可以程式化的金融,是提供金融自由度的一種方式。智慧合約DeFi是執行在去中心化網路上(例如以太坊[4]),由智慧合約(例如USD幣:一種區塊鏈上美元代幣)驅動的。智慧合約其實是很好理解的,Nick Szabo是數字貨幣和加密學的先驅者,在1997年他[最早提出智慧合約]((https://www.fon.hum.uva.nl/rob/Courses/InformationInSpeech/CDROM/Literature/LOTwinterschool2006/szabo.best.vwh.net/idea.html "最早提出智慧合約") )並將其比喻為自動販賣機。自動販賣機就如一個被植入自動化程式的合約,他有如下特點:1. 你按照顯示的金額放入貨幣,機器會給你飲料;2. 你不按照顯示的金額付款,你拿不到飲料;
3. 如果你付了應付金額,但是機器沒給你飲料,亦或是你在沒付錢的情況下機器給了你飲料,這些都是違反自動販賣機的規則。自動販賣機可以在無人干涉情況下,很好的履行他的合約精神。現代智慧合約[5]工作原理也是類似的,合約的條件是用可執行的程式碼來表達的。去中心化網路保證按要求執行,並且任何人都不能破壞規則或者篡改結果。因為網路會一字不差地執行程式碼,有瑕疵的智慧合約會產生預想不到的後果。(“程式碼是條例”)把握當下很多人覺得在區塊鏈上去搭建應用比較困難,認為只有高階玩家可以嘗試。但是近幾年出現來了很多工具,開發者介面,幫助程式設計能力一般的人去實現構建。

最近,DeFi[6]生態呈現爆發式地增長。USDC不到2年捕獲的總價值達到10億美元[7],同時各種各樣的DeFi服務在不到3年的時間,總價值超過20億美金。當下可謂是DeFi發展的最佳時機。

下面的教程主要目的是介紹如何開發自己的DeFi智慧合約。我們希望,本教程可以幫助建立一個全球、開放的金融體系。

開始

本系列教程假設你有使用JavaScript[8]的經驗,這是世界上使用最廣泛的程式語言。你還將學習Solidity[9],Ethereum[10]上使用的智慧合約程式語言。最後,你也會認識USDC[11],這是DeFi應用程式中最廣泛採用的由法幣支援的穩定代幣。

設定開發環境

首先,我們需要一個類unix的環境,並在上面安裝Node.js v12.x[12] (LTS的最新版本)。macOS本身就是Unix環境,Windows使用者可以透過從微軟商店安裝Ubuntu on WSL[13]來獲得它。更詳細的步驟macOS可以檢視這裡[14],Windows檢視這裡[15]。對於文字編輯器,強烈推薦使用Visual Studio Code[16],因為你將使用的專案模板是預先配置的,但你可以使用任何編輯器。哦,我更喜歡Vim的快捷鍵繫結方式[17]。

建立專案

建立一個Solidity專案需要一些工作,而且老實說,在這個階段我們不希望被搭建專案瑣碎的工作而分心了,所以已經為你準備了一個預配置模板[18]。

透過在終端中執行以下命令下載和設定模板:

$ git clone https://github.com/CoinbaseStablecoin/solidity-tutorial.git
$ cd solidity-tutorial
$ npm install -g yarn        # Install yarn package manager
$ yarn                       # Install project dependencies

當yarn在安裝的時候,你可能會看到一些編譯錯誤。你可以忽略這些錯誤。當你最後看到“完成”資訊,你就可以開始了。

在Visual Studio Code開啟專案

在Visual Studio Code中開啟專案資料夾(solidity-tutorial)。專案第一次開啟時,Visual Studio Code可能會提示你安裝擴充套件。繼續並點選“安裝所有”,這將增加各種有用的擴充套件,如程式碼自動格式化和solidity語法高亮。

在以太坊建立賬戶

在以太坊上做任何事情之前,你需要有一個帳戶。賬戶通常被稱為“錢包”,因為它們可以包含像ETH和USDC這樣的數字資產。終端使用者通常使用以太坊錢包應用,像Coinbase錢包[19]或Metamask[20]來建立錢包,但透過程式使用ethers.js[21]方式建立一個賬戶也很簡單。

在src目錄下,建立一個新的js檔案createWallet.js,寫入如下程式碼:

const ethers = require("ethers");

const wallet = ethers.Wallet.createRandom();

console.log(`Mnemonic: ${wallet.mnemonic.phrase}`);
console.log(`Address: ${wallet.address}`);

儲存檔案,然後使用Node.js來執行檔案

$ node src/createWallet.js

Mnemonic: rabbit enforce proof always embrace tennis version reward scout shock license wing
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a

剛才發生了什麼?好吧,你得到了一個全新的Ethereum賬號。“mnemonic”是“助記符”或被稱為的“恢復短語”,是用於帳戶執行操作所需的加密金鑰,地址是帳戶的名稱。記得把它們寫下來。另外,為了防止你們使用我的助記符,我已經做了輕微的修改,請使用你自己的!

可以把這些看作是密碼和銀行賬戶的帳號,不過錢包地址可以在幾秒鐘內建立一個,而且你不需要填寫申請表格或分享任何個人資訊。而且你可以在任何地方執行此程式碼。

助記符必須保密。如果你丟失了它,你將永遠無法訪問你的帳戶和帳戶中儲存的任何資產,沒有人能夠幫助你!把它放在安全的地方!

從技術上講,你並沒有真正“創造”一個帳戶本身。相反,你建立的是一個私有/公共金鑰對。如果你好奇到底發生了什麼,可以看下橢圓曲線密碼學[22],比特幣和以太坊規範BIP39[23], BIP32[24],EIP55[25]及其在本專案中[26]的實現。

關於Gas和挖礦

以太坊是一個去中心化的網路,由世界各地成千上萬臺計算機組成,但是它們並不是免費執行的。要在區塊鏈上執行變更狀態,如儲存和更新資料,你必須用用ETH向網路支付交易費,在以太坊上也稱為“gas”。gas[27]費用和增加新區塊獲得的獎金就是激勵礦工運算的激勵。這個過程被稱為“挖礦”,不斷做運算的被稱為“挖礦者”。我們將在稍後的教程中再次討論這個問題(gas,gas價格和gas限額)。

獲得測試網路ETH

現在你有了賬戶,你應該存一些ETH。在開發的時候我們不想浪費真正的ETH,所以我們需要一些ETH用於在測試網路開發和測試網路(“testnet”)。現在有許多不同的Ethereum測試網路,我們將會使用Ropsten,因為獲得測試代幣比較容易。首先,讓我們使用Etherscan[28]檢查當前餘額,這是一個以太坊的區塊資訊的瀏覽器。你可以在瀏覽器中輸入以下URL,將你的地址替換為之前建立的地址,以0x開始。

https://ropsten.etherscan.io/address/**YOUR_ADDRESS**[29]

來源:* [*ropsten.etherscan.io*](https://ropsten.etherscan.io/ "*ropsten.etherscan.io*")

你可以看到現在餘額是0。保持該頁面開啟,並在另一個頁面中開啟Ropsten Ethereum Faucet[30]。在第二個頁面中,輸入你的地址,然後點選“傳送我(Send me)”按鈕。完成後可能只需要幾秒鐘到一兩分鐘。稍後再次檢查Etherscan,你應該會看到新的餘額為1ETH和轉入交易。

透過程式設計獲取ETH餘額

連線以太坊網路

我們可以使用Etherscan檢視餘額,但是使用程式碼也可以很容易檢視餘額。在我們寫程式碼之前,我們需要連線到以太坊網路。有許多方法可以實現,包括在自己的計算機上執行一個網路節點,但到目前為止,最快和最簡單的方法是透過一個託管節點來實現,例如INFURA[31]或Alchemy[32]。前往INFURA[33],建立一個免費帳戶並建立一個新專案來獲取API金鑰(專案ID)。

Go Ethereum (“geth”)[34] 和 Open Ethereum[35](之前被稱為Parity Ethereum)。這兩個是最為廣泛使用地節點軟體。

透過程式碼檢視ETH餘額

首先,透過讀取助記符進入到我們的賬戶中。在src資料夾下,建立一個名為wallet.js的JavaScript檔案。敲入以下程式碼:

const ethers = require("ethers");

// 在這裡替換你自己的助記符

const mnemonic =
  "rabbit enforce proof always embrace tennis version reward scout shock license wing";
const wallet = ethers.Wallet.fromMnemonic(mnemonic);

console.log(`Mnemonic: ${wallet.mnemonic.phrase}`);
console.log(`Address: ${wallet.address}`);

module.exports = wallet;

用你自己的字串替換程式碼中的助記符字串。請注意,在生產中,助記符不應該像這樣直接寫在程式碼中。理想的是它從配置檔案或環境變數中讀取,這樣它就不會因為寫在原始碼中而洩漏。

執行程式碼,你應該能夠看到和之前相同的地址

$ node src/wallet.js
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a

接下來,在同一個資料夾中,建立一個名為provider.js的新檔案。在這個檔案中,我們將使用前面獲得的INFURA API金鑰。記得替換成你自己的api key:

const ethers = require("ethers");

const provider = ethers.getDefaultProvider("ropsten", {
  // 替換INFURA API KEY
  infura: "0123456789abcdef0123456789abcdef",
});

module.exports = provider;

最後,我們會引用wallet.js和provider.js,在同一目錄下建立新的檔案getBalance.js

const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
  const account = wallet.connect(provider);
  const balance = await account.getBalance();
  console.log(`ETH Balance: ${ethers.utils.formatUnits(balance, 18)}`);
}

main();

執行程式碼,你就可以看到餘額了

$ node src/getBalance.js
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
ETH Balance: 1.0

代幣換算

我們剛剛建立的程式碼非常容易理解,但是你會想知道**ethers.utils.formatUnits(balance, 18)**的作用。嗯,ETH實際上有18位,最小的單位叫“wei”(發音為“way”)。換句話說,一個ETH等於1000,000,000,000,000,000,000 wei。另一個常見的單位是Gwei(發音為“Giga-way”),也就是1,000,000,000 wei。getBalance方法是以wei中返回了結果,因此我們必須透過將結果除以10的18次方將其轉換回ETH。你可以在這裡[36]找到全部的單位名稱。

你也可以使用 ethers.utils.formatEther(balance), 相當於**ethers.utils.formatUnits(balance, 18)**的簡寫.

獲得測試網路的USDC

你賬戶裡的只有ETH,略顯孤單,所以我們打算增加一些USDC。我已經在Ropsten testnet上部署了一個偽USDC智慧合約[37]。雖然我們沒有專門獲得免費USDC的網站,但是在合約中已經包含了該功能,當你呼叫它時,它會給你一些免費的testnet USDC。你可以在Etherscan中的合約程式碼欄目[38]找到合約,並在合約原始碼中搜尋gimmeSome。我們將呼叫這個函式來將一些USDC傳送到我們的帳戶。

發起交易來呼叫智慧合約

在以太坊的智慧合約中有主要有兩類方法:讀寫和只讀。第一種方式可以修改區塊鏈上的資料,而第二種僅僅是讀取區塊鏈上的資料,但是不能修改資料。 只讀方法不用透過交易來呼叫,所以不會耗費ETH,除非是在讀寫方法中的一部分。讀寫方法是一定要透過交易來呼叫,所以一定會消耗ETH。呼叫gimmeSome方法會改變USDC數量的改變,所以必須透過一次交易來完成。

呼叫智慧合約的方法需要再多些步驟,但是也不復雜。第一,需要知道呼叫方法的完整介面,被稱為函式簽名或函式原型。我們看下gimmeSome方法的原始碼如下:

function gimmeSome() external

這是一個沒有任何引數的方法,而且被標記為external,表示只能從外部可以呼叫,不能被合約內的其他方法呼叫。這個對我們來說不影響,因為我們就是從外部呼叫。

在主鏈上的真實的USDC合約[39]是沒有gimmeSome 方法的

在src 資料夾下建立一個新檔案,命名為getTestnetUSDC.js,然後輸入以下程式碼

const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
  const account = wallet.connect(provider);

  const usdc = new ethers.Contract(
    "0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4",
    ["function gimmeSome() external"],
    account
  );

  const tx = await usdc.gimmeSome({ gasPrice: 20e9 });
  console.log(`Transaction hash: ${tx.hash}`);

  const receipt = await tx.wait();
  console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
  console.log(`Gas used: ${receipt.gasUsed.toString()}`);
}

main();

程式碼開始部分, 使用我們感興趣的gimmeSome的介面和測試網路的地址USDC合約0x68ec⋯69c4[40]地址例項化了一個合約物件(new ethers.Contract)。 這個方法是不需要任何引數,但是你可以在最後加入一個引數。這次我20 Gwei的gas費,來加快交易打包速度。與網路互動的所有方法在本質上是非同步的,返回一個**Promise**[41],所以我們使用JavaScript的**await**[42]。完成後會返回交易的hash值,這是用於檢視交易的惟一識別符號。

執行該程式碼,你將看到如下內容:

$ node src/getTestnetUSDC.js

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Transaction hash: 0xd8b4b06c19f5d1393f29b408fc0065d0774ec3b4d11d41be9fd72a8d84cb6208
Transaction confirmed in block 8156350
Gas used: 35121

好的,祝賀你透過程式碼的方式完成了第一次ETH的交易。在Ropsten Etherscan[43]檢視下你的賬戶地址和交易hash。你應該可以檢視到,賬戶裡有10個測試USDC,ETH的餘額小於1,因為支付了gas費用。

如果你在看Etherscan交易,你會發現這是一筆傳送0個ETH連同4個位元組的資料到合約地址。如果呼叫方法時有引數,就會有超過4位元組的資料。如果你想了解該資料是如何編碼的,請閱讀Ethereum合約ABI規範[44]。

Gas,Gas費用 和 Gas限制

之前我提到過,我們給這筆交易20Gwei的gas價格來加快交易速度,程式也顯示了使用的gas的量。這一切意味著什麼?嗯,以太坊是由網路運營商組成的網路。可以把它想象成一臺世界計算機。這不是一臺免費的電腦,你在這臺電腦上執行的每條指令都要花錢。這臺電腦也被全世界的人共享,這意味著每個人都必須互相競爭,以獲得他們使用這臺電腦的時間。

我們怎樣才能做到公平呢?嗯,我們可以把這臺電腦上的時間進行拍賣,你願意出的價越高,你執行的效率也更快。這當然不是十全十美的,因為可能會導致只有有很多ETH的人才有特權使用這個電腦。然而,在系統變得更可擴充套件並能夠容納更多交易之前,這是我們可以選擇的一個可行解決方案。

回到區塊鏈術語上來, “gas used”是在完成交易所消耗的計算資源的數量,“gas price”是你願意為每一單位gas支付的價格。一般來說,你願意支付的金額越高,你的交易優先順序就越高,透過網路確認的速度也就越快。上面我們使用20 Gwei作為gas價格,所使用的gas為35,121(可以在Etherscan中檢視交易),所以總共使用gas費用為35,121 * 20 Gwei = 702,420 Gwei或0.00070242 ETH。

因為gas需要消耗金錢,你可能想要設定你願意花費的最多gas。幸運的是,你可以透過“gas limit”設定。如果交易最終需要的gas超過規定的限額,交易就會失敗,而不會繼續執行。需要注意的是如果交易因為gas限額而失敗,已經花費的gas將不會退還給你。

透過呼叫智慧合約讀取資料

你可以在Etherscan上檢視到收到了10個USDC,讓我們透過程式碼檢查餘額來確認這一點。

我們修改下src資料夾下的getBalance.js檔案

const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main() {
  const account = wallet.connect(provider);

  // 定義合約介面
  const usdc = new ethers.Contract(
    "0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4",
    [
      "function balanceOf(address _owner) public view returns (uint256 balance)",
    ],
    account
  );

  const ethBalance = await account.getBalance();
  console.log(`ETH Balance: ${ethers.utils.formatEther(ethBalance)}`);

  // 呼叫balanceOf方法
  const usdcBalance = await usdc.balanceOf(account.address);
  console.log(`USDC Balance: ${ethers.utils.formatUnits(usdcBalance, 6)}`);
}

main();

USDC是ERC20代幣,因此它包含ERC20規範[45]中定義的所有方法。balanceOf就是其中之一,它的介面直接來自規範定義的。 balanceOf是一個只讀函式,所以它可以免費呼叫。最後,值得注意的是,USDC使用6位小數精度,而其他許多ERC20代幣使用18位小數。

你可以在這裡[46]瞭解更多關於Solidity方法。

執行以下程式碼,你就可以看到USDC餘額

$ node src/getBalance.js

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
ETH Balance: 0.9961879
USDC Balance: 10.0

ETH和USDC轉賬

現在我們來看看怎麼可以使用賬戶中的ETH和USDC

使用ETH

在src資料夾下建立transferETH.js檔案

const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main(args) {
  const account = wallet.connect(provider);
  let to, value;

  // 生成第一個引數——接受地址
  try {
    to = ethers.utils.getAddress(args[0]);
  } catch {
    console.error(`Invalid recipient address: ${args[0]}`);
    process.exit(1);
  }

    // 生成第二個引數——數量
  try {
    value = ethers.utils.parseEther(args[1]);
    if (value.isNegative()) {
      throw new Error();
    }
  } catch {
    console.error(`Invalid amount: ${args[1]}`);
    process.exit(1);
  }
  const valueFormatted = ethers.utils.formatEther(value);

  //檢查賬戶有足夠餘額
  const balance = await account.getBalance();
  if (balance.lt(value)) {
    const balanceFormatted = ethers.utils.formatEther(balance);

    console.error(
      `Insufficient balance to send ${valueFormatted} (You have ${balanceFormatted})`
    );
    process.exit(1);
  }

  console.log(`Transferring ${valueFormatted} ETH to ${to}...`);

  // 提交轉賬
  const tx = await account.sendTransaction({ to, value, gasPrice: 20e9 });
  console.log(`Transaction hash: ${tx.hash}`);

  const receipt = await tx.wait();
  console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
}

main(process.argv.slice(2));

這段程式碼雖然比前面的程式碼長,但實際上只是將之前所學的程式碼組合起來。這段程式碼中要有兩個命令列引數。第一個是接收者地址,第二個是要傳送的金額。然後確保提供的地址是有效的,提供的金額不是負數,並且帳戶有足夠的餘額能夠傳送請求的金額。然後,提交交易並等待它被確認。

用之前的createWallet.js建立一個新賬戶,然後嘗試向這個地址轉些ETH

$ node src/createWallet.js

Mnemonic: napkin invite special reform cheese hunt refuse ketchup arena bag love caution
Address: 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13

$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 0.1

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Transferring 0.1 ETH to 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13...
Transaction hash: 0xa9f159fa8a9509ec8f8afa8ebb1131c3952cb3b2526471605fd84e8be408cebf
Transaction confirmed in block 8162896

你可以在Etherscan[47]看到結果,我們再來測試驗證邏輯是有效的。

$ node src/transferETH.js foo

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid address: foo

$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 0.1.2
Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid amount: 0.1.2

$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 -0.1

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Invalid amount: -0.1

$ node src/transferETH.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 100

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Insufficient balance to send 100.0 (You have 0.89328474)

USDC轉賬

上面很大一部的程式碼可以用到這裡,主要的區別是USDC是精確到6位,還有你是使用ERC20 規範中的transfer。入參依然是“to” 及 “value”,然後呼叫智慧合約的transfer 方法。

在同一檔案下建立transferUSDC.js檔案

const ethers = require("ethers");
const wallet = require("./wallet");
const provider = require("./provider");

async function main(args) {
  const account = wallet.connect(provider);

  // 在合約中定義balanceOf和transfer方法
  const usdc = new ethers.Contract(
    "0x68ec573C119826db2eaEA1Efbfc2970cDaC869c4",
    [
      "function balanceOf(address _owner) public view returns (uint256 balance)",
      "function transfer(address _to, uint256 _value) public returns (bool success)",
    ],
    account
  );

  let to, value;

    // 生成第一個引數——接受地址
  try {
    to = ethers.utils.getAddress(args[0]);
  } catch {
    console.error(`Invalid address: ${args[0]}`);
    process.exit(1);
  }

    // 生成第二個引數——數量
  try {
    value = ethers.utils.parseUnits(args[1], 6);
    if (value.isNegative()) {
      throw new Error();
    }
  } catch {
    console.error(`Invalid amount: ${args[1]}`);
    process.exit(1);
  }
  const valueFormatted = ethers.utils.formatUnits(value, 6);

  //檢查賬戶有足夠餘額
  const balance = await usdc.balanceOf(account.address);
  if (balance.lt(value)) {
    const balanceFormatted = ethers.utils.formatUnits(balance, 6);

    console.error(
      `Insufficient balance to send ${valueFormatted} (You have ${balanceFormatted})`
    );
    process.exit(1);
  }

  console.log(`Transferring ${valueFormatted} USDC to ${to}...`);

  // 提交轉賬,呼叫transfer方法
  const tx = await usdc.transfer(to, value, { gasPrice: 20e9 });
  console.log(`Transaction hash: ${tx.hash}`);

  const receipt = await tx.wait();
  console.log(`Transaction confirmed in block ${receipt.blockNumber}`);
}

main(process.argv.slice(2));

試一試,你應該可以看到以下結果:

$ node src/transferUSDC.js 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13 1

Address: 0xB3512cF013F71598F359bd5CA3f53C1F4260956a
Transferring 1.0 USDC to 0xDdAC089Fe56F0a9C70e6a04C74DCE52F86a91e13...
Transaction hash: 0xc1b2157a83f29d6c04f960bc49e968a0cd2ef884761af7f95cc83880631fe4af
Transaction confirmed in block 8162963

恭喜

在本教程中,你學習瞭如何生成錢包、查詢餘額、轉移代幣和呼叫智慧合約。你可能覺得自己還不太瞭解區塊鏈,不過你已經有足夠的知識,去構建自己加密錢包應用程式。為了保持簡單,我們一直在編寫命令列指令碼,那麼是否可以嘗試構建一個圖形介面的網頁呢?

在本教程系列的下一部分中,我們將從頭開始用solidity編寫智慧合約,並學習如何構建自己的硬幣,可與USDC交換。我們還將使用今天學到的技術來與我們構建的合約進行互動。請繼續關注。

參考資料
[1]登鏈翻譯計劃: https://github.com/lbc-team/Pioneer
[2]DIFENG: https://learnblockchain.cn/people/1234
[3]Tiny熊: https://learnblockchain.cn/people/15
[4]以太坊: https://learnblockchain.cn/2017/11/20/whatiseth
[5]智慧合約: https://learnblockchain.cn/2018/01/04/understanding-smart-contracts
[6]DeFi: https://learnblockchain.cn/article/1185
[7]USDC不到2年捕獲的總價值達到10億美元: https://medium.com/centre-blog/usdc-market-cap-exceeds-1-billion-fastest-growing-digital-dollar-stablecoin-to-do-so-c5ba314474ca
[8]JavaScript: https://en.wikipedia.org/wiki/JavaScript
[9]Solidity: https://solidity.readthedocs.io/
[10]Ethereum: https://ethereum.org/
[11]USDC: https://www.coinbase.com/usdc
[12]Node.js v12.x: https://nodejs.org/
[13]Ubuntu on WSL: https://ubuntu.com/wsl

[14]這裡: https://treehouse.github.io/installing-guides/mac/nod-mac.html

[15]這裡: https://docs.microsoft.com/en-us/windows/nodejs/setup-on-wsl2

[16]Visual Studio Code: https://code.visualstudio.com/

[17]Vim的快捷鍵繫結方式: https://xkcd.com/378/

[18]預配置模板: https://github.com/coinbasestablecoin/solid-tutorial]

[19]Coinbase錢包: https://wallet.coinbase.com/

[20]Metamask: https://metamask.io/

[21]ethers.js: https://github.com/ethers-io/ethers.js/

[22]橢圓曲線密碼學: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography

[23]BIP39: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki

[24]BIP32: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki

[25]EIP55: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md

[26]在本專案中: https://github.com/petejkim/wallet.ts

[27]gas: https://learnblockchain.cn/2019/06/11/gas-mean

[28]Etherscan: https://ropsten.etherscan.io/

[29]https://ropsten.etherscan.io/address/YOUR_ADDRESS: https://ropsten.etherscan.io/address/YOUR_ADDRESS

[30]Ropsten Ethereum Faucet: https://faucet.ropsten.be/

[31]INFURA: https://infura.io/

[32]Alchemy: https://alchemyapi.io/

[33]INFURA: https://infura.io/

[34]Go Ethereum (“geth”): https://geth.ethereum.org/

[35]Open Ethereum: https://github.com/openethereum/openethereum#readme&

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;