반응형

1. Install ehthers zkSync

npm install ethers zkSync

2. zkSync 란?

zkSync 는 EVM 을 확장하기 위한 레이어 2 솔루션. ZKP 를 이용한 빠르고 저렴한 거래처리 가능

ZKP (Zero Knowledge Proof) 는 뭔데?

어떤 정보를 드러내지 않고 특정 주장이나 사실이 참임을 증명하는 기술

블록체인에서는 거래내역을 보여주지 않고 거래내역이 유효함을 증명할 수 있다.

zkSync 를 통해 빠르고 저렴한 거래처리를 적용할 수 있다!

Provider 생성

alice.js, bob.js 를 zkSync 로 연결하기 위해서는 util.js 에서 zkSync Provider를 생성해야한다.

// utils.js

async function getZkSyncProvider(zksync, networkName) {
    let zkSyncProvider;
    try {
        zkSyncProvider = await zksync.getDefaultProvider(networkName);
    } catch (error) {
        console.log('Unable to connect to zkSync.');
        console.error(error);
    }
    return zkSyncProvider;
}

3. zkSync 계정 만들기

zkSync 와 이더리움을 연결하려면 계정과 지갑에 대해 알아야한다. 따라서, zkSync 의 지갑 계정을 생성해 이더리움과 연결해보자.

async function getZkSyncProvider (zksync, networkName) {
  let zkSyncProvider
  try {
    zkSyncProvider = await zksync.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Unable to connect to zkSync.')
    console.log(error)
  }
  return zkSyncProvider
}

async function getEthereumProvider (ethers, networkName) {
  let ethersProvider
  try {
    ethersProvider = new ethers.getDefaultProvider(networkName)
  } catch (error) {
    console.log('Could not connect to Rinkeby')
    console.log(error)
  }
  return ethersProvider
}

async function initAccount (rinkebyWallet, zkSyncProvider, zksync) {
  const zkSyncWallet = await zksync.Wallet.fromEthSigner(rinkebyWallet, zkSyncProvider)
  return zkSyncWallet
}

 

위 코드의 결과로 zkSyncWallet 을 생성할 수 있다.

4. zkSync 인증 확인하기

zkSync 계정을 생성했다면 인증 여부를 확인해야한다. 지갑의 key 가 존재한다면 key 가 singing key에 등록이 되어있는지 확인 후 changePubkey 에 저장한다.

async function registerAccount (wallet) {
  console.log(`Registering the ${wallet.address()} account on zkSync`)
  if(!await wallet.isSigningKeySet()){
    if(await wallet.getAccountId() === undefined){
        throw new Error('Unknown account')
    }
    const changePubkey = await wallet.setSigningKey()
    await changePubkey.awaitReceipt()
  }
}

5. zkSync 에 재화를 넣어보자

Priority Operations

  • 이더리움 네트워크에서 작동되는 작업들

Transactions

  • zkSync 에서 사용되는 활동들

zkSync에서 transaction 이 사용되는 원리

zkSync 에 트랜잭션을 보낼때 Promise 를 사용하게 된다. 트랜잭션의 작업을 믿는다면 따로 블록이 생성될 때까지 기다릴 필요없다.

zkSync 트랜잭션은 SNARK 증명 방식으로 검증하고, 이더리움 스마트 컨트랙트에 등록될 때까지 10분 소요된다.

async function depositToZkSync(zkSyncWallet, token, amountToDeposit, ethers){
    const deposit = await zkSyncWallet.depositToSyncFromEthereum({
        depositTo: zkSyncWallet.address(),
        token: token,
        amount: ethers.utils.parseEther(amountToDeposit)
    })
    try{
        await deposit.awaitReceipt()
    }catch(error){
        console.log('Error while awaiting confirmation from the zkSync operators.')
        console.log(error)
    }
}
  • deposit 객체를 만들기 위해 이더리움 wei 단위로 변경해야한다.
  • awaitReceipt 함수를 이용해 트랜잭션이 정상 작동한지 확인 한다.

6. zkSync 에서 애셋 옮기기

애셋을 옮기기 위해서는 두 가지 과정으로 나눌 수 있다.

  1. syncTransfer 함수를 통해 zkSync 지갑과 수취인의 주소, 애셋 정보 및 fee 를 전달할 수 있다.
  2. awaitReceipt 를 통해 정상 작동했는지 확인할 수 있다.

zkSync에서 전송 작업의 정밀도는 제한되어 있으므로 전송량은 5바이트 길이의 부동 소수점

지불한 수수료는 2바이트 길이의 부동 소수점 표현 으로 패키징해야한다.

async function transfer (from, toAddress, amountToTransfer, transferFee, token, zksync, ethers) {
    const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToTransfer))
    const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(transferFee))
    const transfer = await from.syncTransfer(
        {
            to:toAddress,
            token: token,
            amount : closestPackableAmount,
            fee:closestPackableFee
        }
    )
    const transferReceipt = await transfer.awaitReceipt()
    console.log('Got transfer receipt.')
    console.log(transferReceipt)
}

7. Transfer Fee

Transfer fee 를 계산하는 방법을 배우자

fee 의 종류

  1. off-chain fee
    • 계산 및 저장 비용을 뜻하며, 불가변성이다.
  2. on-chain fee
    • 이더리움에서 SNARK 를 검증하는데 드는 비용이다. 가스 가격에 따라 달라지기에 가변적이다.
async function getFee(transactionType, address, token, zkSyncProvider, ethers){
    const feeInWei = await zkSyncProvider.getTransactionFee(transactionType, address, token)
    return ethers.utils.formatEther(feeInWei.totalFee.toString())
}
  • getTransactionFee 로 구한 wei 는 매우 길기 때문에 formatEther 함수로 처리해준다.

8. Withdraw to Ethereum

세 가지 과정으로 Withdraw 할 수 있다.

  1. closestPackableAmount 를 계산
  2. wallet.withdrawFromSyncToEthereum 으로 withdraw를 계산
  3. Receipt 검증 완료
async function withdrawToEthereum (wallet, amountToWithdraw, withdrawalFee, token, zksync, ethers) {
    const closestPackableAmount = zksync.utils.closestPackableTransactionAmount(ethers.utils.parseEther(amountToWithdraw))
    const closestPackableFee = zksync.utils.closestPackableTransactionFee(ethers.utils.parseEther(withdrawalFee))
    const withdraw = await wallet.withdrawFromSyncToEthereum({
        ethAddress : wallet.address(),
        token : token,
        amount : closestPackableAmount,
        fee : closestPackableFee
    })
    await withdraw.awaitVerifyReceipt()
  console.log('ZKP verification is complete')
}

9. Account Balances

계정의 Balance 에는 두가지가 존재한다.

  1. commit balance
    1. 거래 내역을 zkSync 의 스마트 컨트랙트에 포함시킨다.
    2. 즉시 사용 가능
    3. 이더리움 메인넷에 기록되지만 검증되지 않았다.
  2. verify balance
    1. 블록을 최종 검증 상태로 확정
    2. SNARK 로 블록을 검증
    3. 최종적으로 보장된 verify 금액
async function displayZkSyncBalance (wallet, ethers){
    const state = await wallet.getAccountState()
    if(state.committed.balances.ETH){
        console.log(`Commited ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.committed.balances.ETH)}`)
    }else{
        console.log(`Commited ETH balance for ${wallet.address()}: 0`)
    }
    if(state.verified.balances.ETH){
        console.log(`Verified ETH balance for ${wallet.address()}: ${ethers.utils.formatEther(state.verified.balances.ETH)}`)
    }else{
        console.log(`Verified ETH balance for ${wallet.address()}: 0`)
    }
}

10. 가게주인 밥 - 블록체인과 연결

위에서 만든 utils.js 를 이용해 클라이언트와 연결해보자

(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')

  // Start here
  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
})()
  • zkSyncProvider, ethersProvider 을 util.js 에서 불러와야 한다.

11. 가게주인 밥 - 밥의 지갑 주소를 가져오다

const bobRinkebyWallet = new ethers.Wallet(process.env.BOB_PRIVATE_KEY, ethersProvider)
console.log(`Bob's Rinkeby address is: ${bobRinkebyWallet.address}`)
console.log(`Bob's initial balance on Rinkeby is: ${ethers.utils.formatEther(await bobRinkebyWallet.getBalance())}`)
const bobZkSyncWallet = await utils.initAccount(bobRinkebyWallet, zkSyncProvider, zksync) 
  • 밥에게 필요한 지갑주소와 zkSync 지갑 주소를 가져온다.

12. 잔액 업데이트

zkSync 에는 잔액이 업데이트 됐음을 알리는 메커니즘이 없기 때문에 setInterval 을 통해 잔액을 주기적으로 최신화해야한다.

  process.on('SIGINT', () => {
    console.log('Disconnecting')
    // Disconnect
    process.exit()
  })
  setInterval(async () => {
    // Call the `utils.displayZkSyncBalance` function
    await utils.displayZkSyncBalance(bobZkSyncWallet, ethers);
    console.log('---')
  }, SLEEP_INTERVAL)

13. 계좌 등록

상점을 사용할 앨리스의 계좌를 등록해야한다.

앨리스가 상점을 방문한 이후의 시나리오는 다음과 같다.

  1. 앨리스는 zkSync 계좌를 이용해 이더리움을 배포하려고한다.
  2. 퍼블릭 키를 등록해 zkSync 로 거래할 수 있게 하려고 한다.
  3. zkSync 를 이용해 밥과 거래를 할 것 이다.
  4. 이더리움 코인으로 zkSync 에서 이더리움으로 옮겨 남은 금액을 확인해야한다.
(async () => {
  const ethers = require('ethers')
  const zksync = require('zksync')
  const utils = require('./utils')
  const token = 'ETH'
  const amountToDeposit = '0.05'
  const amountToTransfer = '0.02'
  const amountToWithdraw = '0.002'

  const zkSyncProvider = await utils.getZkSyncProvider(zksync, process.env.NETWORK_NAME)
  const ethersProvider = await utils.getEthereumProvider(ethers, process.env.NETWORK_NAME)
  console.log('Creating a new Rinkeby wallet for Alice')
  const aliceRinkebyWallet = new ethers.Wallet(process.env.ALICE_PRIVATE_KEY, ethersProvider) // Account #78
  console.log(`Alice's Rinkeby address is: ${aliceRinkebyWallet.address}`)
  const aliceInitialRinkebyBalance = await aliceRinkebyWallet.getBalance()
  console.log(`Alice's initial balance on Rinkeby is: ${ethers.utils.formatEther(aliceInitialRinkebyBalance)}`)

  console.log('Creating a zkSync wallet for Alice')
  const aliceZkSyncWallet = await utils.initAccount(aliceRinkebyWallet, zkSyncProvider, zksync)

  console.log('Depositing')
  // Start here
    await utils.depositToZkSync(aliceZkSyncWallet, token, amountToDeposit, ethers)
    await utils.displayZkSyncBalance(aliceZkSyncWallet, ethers)
    await utils.registerAccount(aliceZkSyncWallet)
})()

14. zkSync 로 결제해보자

utils.getFee, utils.transfer 함수를 이용해 결제 시나리오를 만들어보자.

const transferFee = await utils.getFee('Transfer', aliceRinkebyWallet.address, token, zkSyncProvider, ethers)
await utils.transfer(aliceZkSyncWallet, process.env.BOB_ADDRESS, amountToTransfer, transferFee,token,zksync,ethers)

15. withdraw

결제와 마찬가지로 withdraw 도 구현할 수 있다.

const withdrawalFee = await utils.getFee('Withdraw', aliceRinkebyWallet.address, token,zkSyncProvider, ethers)
await utils.withdrawToEthereum(aliceZkSyncWallet, amountToWithdraw, withdrawalFee, token,zksync,ethers)
반응형

'BlockChain > Solidity' 카테고리의 다른 글

[DP/NFT] 4. 리액트 세팅  (0) 2022.09.05
[DP/NFT] 3. SaleAnimalToken 작성  (0) 2022.09.05
[DP/NFT] 2. Minting contract 작성  (0) 2022.09.05
[DP/NFT] 1. Install Solidity & Metamask  (0) 2022.08.25

+ Recent posts