본문 바로가기

BlockChain Developer/Public Blockchain

[Ethereum] GSN

GSN

 

0. 개념 알고가기

-> GSN은 Gas Station Network의 약자로, 간단하게 요약하자면 Dapp의 대부분은 이더리움 기반 Dapp이다. 따라서 Dapp을 만들고 Dapp에서 컨트렉트를 호출하기 위해서는 소량의 가스 수수료를 요구한다. 그러나, 새로운 wallet을 만들면 그 사람은 가스가 있는가? 가스가 없다. 그래서 사용자는 항시 Ethereum을 보유해야한다. 즉, UX 또는 사용성이 매우 나빠진다. 하지만 GSN을 활용하면, 가스 비용은 GSN에서 소모된다. 하지만 공짜는 아니며 사이트 운영자가 입금한 금액만을 사용할 수 있다. 통상적으로 대부분의 네트워크는 GSN 네트워크를 배포중에 있으며 우리는 그것을 활용해 GSN Contract를 배포해 사용할 수 있다.

 

Gas Station Network 순서도

 

 

1. Ganache-cli 를 가동시킨다.

 

2. React를 셋팅한다. 필요한 디펜던시는 다음과 같다. (앞서 "최신리액트 셋팅"을 마쳤다면 아래만 다운로드하면 된다.)

yarn add ethers @opengsn/cli @opengsn/provider browserify-fs browserify-zlib path-browserify

아니라면,

yarn add --dev ethers @opengsn/cli @opengsn/provider browserify-fs browserify-zlib path-browserify react-app-rewired crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url buffer

 

2-1 리액트에서 셋팅을 진행한다.

 

(gsnProvider 의 config 부분에 "paymasterAddress :" 부분 반드시 대소문자를 지켜라 삽질 조지게했다.)

//config-web3.js

import Web3 from "web3";
import { RelayProvider } from "@opengsn/provider";
import { ethers } from "ethers";
const PaymasterAddress = "0xeb18472EB957E7BaF098e6B7eeC75695978BcbFc";
export let web3;


export const init = async() => {
    await window.ethereum.enable(); //메타마스크 연결

    //GSN Provider 셋팅
    let gsnProvider = await RelayProvider.newProvider({
        provider : window.ethereum,
        config: {
             paymasterAddress: PaymasterAddress,
        }
    }).init();

    provider = new ethers.providers.Web3Provider(gsnProvider);
    web3 = new Web3(window.ethereum);
    account = await web3.eth.getAccounts();
}

 

3. @opengsn/cli를 활용해 gsn을 가동한다.

npx gsn start

 

 

 

4. contract를 작성 및 배포. (필자는 Remix를 통해 배포함)

 

MyTest.sol

//SPDX-License-Identifier: MIT;

pragma solidity 0.8.7;

import "@opengsn/contracts/src/BaseRelayRecipient.sol";

contract Test is BaseRelayRecipient {
    constructor(address _forwarder){
        _setTrustedForwarder(_forwarder);
    }
    string public override versionRecipient = "2.2.0";

    string data = "hi";

    function get() public view returns(string memory){
        return data;
    }

    function change(string memory _input) public {
        data = _input;
    }
}

NaivePaymaster.sol

pragma solidity ^0.8.7;
pragma experimental ABIEncoderV2;

// SPDX-License-Identifier: MIT OR Apache-2.0

import "@opengsn/contracts/src/forwarder/IForwarder.sol";
import "@opengsn/contracts/src/BasePaymaster.sol";

contract NaivePaymaster is BasePaymaster {
	address public ourTarget;   // The target contract we are willing to pay for

	// allow the owner to set ourTarget
	event TargetSet(address target);

	function setTarget(address target) external onlyOwner {
		ourTarget = target;
		emit TargetSet(target);
	}

	event PreRelayed(uint);
	event PostRelayed(uint);

	function preRelayedCall(
		GsnTypes.RelayRequest calldata relayRequest,
		bytes calldata signature,
		bytes calldata approvalData,
		uint256 maxPossibleGas
	) external override virtual
	returns (bytes memory context, bool) {
		_verifyForwarder(relayRequest);
		(signature, approvalData, maxPossibleGas);
		
		require(relayRequest.request.to == ourTarget);
		emit PreRelayed(block.timestamp);
                return (abi.encode(block.timestamp), false);
	}

	function postRelayedCall(
		bytes calldata context,
		bool success,
		uint256 gasUseWithoutPost,
		GsnTypes.RelayData calldata relayData
	) external override virtual {
                (context, success, gasUseWithoutPost, relayData);
		emit PostRelayed(abi.decode(context, (uint)));
	}

  function versionPaymaster() external virtual view override returns (string memory) {
    return "2.2.0";
  }

}

 

5. GSN Contract에 GSN 적용하기

아래 그림을 참조하여 RelayHub -> TrustForwarder -> setTarget으로 알맞은 주소를 입력해 컨트렉트를 호출한다.

 

 

6. GSN에 있는 Paymaster 주소로 1ETH를 보낸다. (1이상의 이더를 전송시 안받더라..)

 

 

7. React 를 통해 컨트렉트를 호출한다.

  const Call_GSNContract_Change = async() => {

    const test_contract = await new ethers.Contract(TestContract.address, TestContract.abi, provider.getSigner());
    const transaction = await test_contract.change(ChangeData_Input);
    const hash = transaction.hash;
    console.log(`Transaction HASH : ${hash}`);

  }

 

 

 

- 끝 -